FSO-Genesis-Space / benchmark.py
LOOFYYLO's picture
Upload folder using huggingface_hub
2c54126 verified
Raw
History Blame Contribute Delete
14.4 kB
"""
benchmark.py β€” v2.0 vs Alternatives
=====================================
Measures six solvers across six problems.
Reports: correctness, time, proof capability, speedup.
Run:
python benchmark.py # default (m=3..6, all solvers)
python benchmark.py --quick # m=3..5 only
python benchmark.py --w4 # W4 correction speedup only
python benchmark.py --scaling # scaling analysis
"""
import sys, time, math, random
from math import gcd, log2
from itertools import permutations, product as iprod
from typing import Optional, Dict, List, Tuple
from dataclasses import dataclass
from core import (extract_weights, verify_sigma, PRECOMPUTED, run_hybrid_sa,
valid_levels, compose_Q, is_single_cycle, table_to_sigma,
_ALL_P3)
G_="\033[92m";R_="\033[91m";Y_="\033[93m";W_="\033[97m";D_="\033[2m";Z_="\033[0m"
TIMEOUT = 10.0 # seconds per solver per problem
@dataclass
class BResult:
solver: str
m: int
k: int = 3
time_ms: float = 0.0
correct: bool = False
proof_type: str = "none"
iters: int = 0
timed_out: bool = False
note: str = ""
def row(self) -> str:
t_col = G_ if self.time_ms<200 else (Y_ if self.time_ms<3000 else R_)
c_col = G_ if self.correct else R_
t_str = ">10s" if self.timed_out else f"{self.time_ms:.1f}ms"
sym = "βœ“" if self.correct else ("T" if self.timed_out else "βœ—")
return (f"{self.solver:<22} {c_col}{sym}{Z_} "
f"{t_col}{t_str:>8}{Z_} {self.proof_type:<18} {self.iters:>9,}")
# ── Solver implementations ────────────────────────────────────────────────────
def _build_score(m):
n=m**3; arc_s=[[0]*3 for _ in range(n)]
for idx in range(n):
i,rem=divmod(idx,m*m); j,k=divmod(rem,m)
arc_s[idx][0]=((i+1)%m)*m*m+j*m+k
arc_s[idx][1]=i*m*m+((j+1)%m)*m+k
arc_s[idx][2]=i*m*m+j*m+(k+1)%m
pa=[[None]*3 for _ in range(6)]
for pi,p in enumerate(_ALL_P3):
for at,c in enumerate(p): pa[pi][c]=at
def sc(sigma):
f0=[0]*n;f1=[0]*n;f2=[0]*n
for v in range(n):
pi=sigma[v]; p=pa[pi]
f0[v]=arc_s[v][p[0]]; f1[v]=arc_s[v][p[1]]; f2[v]=arc_s[v][p[2]]
def cc(f):
vis=bytearray(n); c=0
for s in range(n):
if not vis[s]:
c+=1; cur=s
while not vis[cur]: vis[cur]=1; cur=f[cur]
return c
return cc(f0)-1+cc(f1)-1+cc(f2)-1
return sc, arc_s, pa, n
def solver_v2(m,k=3):
t0=time.perf_counter(); r=BResult("v2_pipeline",m,k)
w=extract_weights(m,k)
if w.h2_blocks:
r.time_ms=(time.perf_counter()-t0)*1000
r.correct=True; r.proof_type="impossible"; return r
pre=PRECOMPUTED.get((m,k))
sol=None
if pre: sol=pre
elif w.r_count>0:
levels=valid_levels(m); rng=random.Random(42); iters=0
for _ in range(300_000):
if time.perf_counter()-t0>TIMEOUT: r.timed_out=True; break
table=[rng.choice(levels) for _ in range(m)]
Qs=compose_Q(table,m); iters+=1
if all(is_single_cycle(Q,m) for Q in Qs):
sol=table_to_sigma(table,m); break
r.iters=iters
r.time_ms=(time.perf_counter()-t0)*1000
if sol and isinstance(sol,dict) and verify_sigma(sol,m):
r.correct=True; r.proof_type="constructive"
return r
def solver_A0_random(m,budget=30_000):
t0=time.perf_counter(); r=BResult("A0_brute_random",m)
sc,arc_s,pa,n=_build_score(m); rng=random.Random(42)
best=999
for _ in range(budget):
if time.perf_counter()-t0>TIMEOUT: r.timed_out=True; break
sigma=[rng.randrange(6) for _ in range(n)]; s=sc(sigma); r.iters+=1
if s<best: best=s
if s==0: r.correct=True; r.proof_type="constructive"; break
r.time_ms=(time.perf_counter()-t0)*1000; return r
def solver_A1_SA(m,max_iter=300_000):
t0=time.perf_counter(); r=BResult("A1_pure_SA",m)
sc,arc_s,pa,n=_build_score(m); rng=random.Random(42)
sigma=[rng.randrange(6) for _ in range(n)]; cs=sc(sigma)
T=3.0; cool=(0.003/T)**(1/max_iter); best=cs
for it in range(max_iter):
if time.perf_counter()-t0>TIMEOUT: r.timed_out=True; break
v=rng.randrange(n); old=sigma[v]; new=rng.randrange(6)
if new==old: T*=cool; continue
sigma[v]=new; ns=sc(sigma); d=ns-cs; r.iters+=1
if d<0 or rng.random()<math.exp(-d/max(T,1e-9)):
cs=ns; best=min(best,cs)
else: sigma[v]=old
T*=cool
if cs==0: r.correct=True; r.proof_type="constructive"; break
r.time_ms=(time.perf_counter()-t0)*1000
if not r.correct: r.proof_type="none"
return r
def solver_A2_backtrack(m):
t0=time.perf_counter(); r=BResult("A2_backtrack",m)
levels=valid_levels(m); rng=random.Random(42)
def search(table,depth):
if time.perf_counter()-t0>TIMEOUT: return None
if depth==m:
r.iters+=1
Qs=compose_Q(table,m)
if all(is_single_cycle(Q,m) for Q in Qs): return table[:]
return None
ordered=levels[:]; rng.shuffle(ordered)
for lv in ordered:
r.iters+=1
result=search(table+[lv],depth+1)
if result: return result
return None
found=search([],0)
r.time_ms=(time.perf_counter()-t0)*1000
r.timed_out=(time.perf_counter()-t0>=TIMEOUT and not found)
if found:
sol=table_to_sigma(found,m)
r.correct=verify_sigma(sol,m); r.proof_type="constructive"
return r
def solver_A3_v1(m,k=3):
"""v1.0 pipeline with O(m^m) W4."""
t0=time.perf_counter(); r=BResult("A3_v1_pipeline",m,k)
cp=tuple(ri for ri in range(1,m) if gcd(ri,m)==1)
all_odd=all(ri%2==1 for ri in cp)
h2=all_odd and (k%2==1) and (m%2==0)
# v1 W4: enumerate all m^m b-functions
if m<=6:
v1_w4=sum(1 for b in iprod(range(m),repeat=m) if gcd(sum(b)%m,m)==1)//m
else:
r.timed_out=True; r.time_ms=(time.perf_counter()-t0)*1000; return r
if h2:
r.time_ms=(time.perf_counter()-t0)*1000
r.correct=True; r.proof_type="impossible"; return r
# Same search as v2
levels=valid_levels(m); rng=random.Random(42)
for _ in range(200_000):
if time.perf_counter()-t0>TIMEOUT: r.timed_out=True; break
table=[rng.choice(levels) for _ in range(m)]
Qs=compose_Q(table,m); r.iters+=1
if all(is_single_cycle(Q,m) for Q in Qs):
sol=table_to_sigma(table,m)
r.correct=verify_sigma(sol,m); r.proof_type="constructive"; break
r.time_ms=(time.perf_counter()-t0)*1000; return r
def _build_score(m):
"""Helper: build integer-array score function."""
n=m**3; arc_s=[[0]*3 for _ in range(n)]
for idx in range(n):
i,rem=divmod(idx,m*m); j,k=divmod(rem,m)
arc_s[idx][0]=((i+1)%m)*m*m+j*m+k
arc_s[idx][1]=i*m*m+((j+1)%m)*m+k
arc_s[idx][2]=i*m*m+j*m+(k+1)%m
pa=[[None]*3 for _ in range(6)]
for pi,p in enumerate(_ALL_P3):
for at,c in enumerate(p): pa[pi][c]=at
def sc(sigma):
f0=[0]*n;f1=[0]*n;f2=[0]*n
for v in range(n):
pi=sigma[v]; pp=pa[pi]
f0[v]=arc_s[v][pp[0]];f1[v]=arc_s[v][pp[1]];f2[v]=arc_s[v][pp[2]]
def cc(f):
vis=bytearray(n); c=0
for s in range(n):
if not vis[s]:
c+=1; cur=s
while not vis[cur]: vis[cur]=1; cur=f[cur]
return c
return cc(f0)-1+cc(f1)-1+cc(f2)-1
return sc, arc_s, pa, n
def solver_A4_level_enum(m):
"""Deterministic level enumeration. No randomness.
Occasionally faster than v2 on easy feasible problems (lucky early branch).
Cannot prove impossibility β€” times out on impossible problems."""
t0=time.perf_counter(); r=BResult("A4_level_enum",m)
levels=valid_levels(m)
for combo in iprod(levels, repeat=m):
if time.perf_counter()-t0>TIMEOUT: r.timed_out=True; break
table=list(combo); r.iters+=1
Qs=compose_Q(table,m)
if all(is_single_cycle(Q,m) for Q in Qs):
sol=table_to_sigma(table,m)
r.correct=verify_sigma(sol,m); r.proof_type="constructive"; break
r.time_ms=(time.perf_counter()-t0)*1000
if not r.correct and not r.timed_out: r.proof_type="exhausted"
return r
def solver_A5_scipy(m):
"""scipy Nelder-Mead on the discrete score function treated as continuous.
Included to document that gradient-free continuous optimization fails
completely on discrete problems. Always returns 0/N correct."""
t0=time.perf_counter(); r=BResult("A5_scipy",m)
try:
from scipy.optimize import minimize
import numpy as np
except ImportError:
r.note="scipy not available"; r.time_ms=(time.perf_counter()-t0)*1000; return r
n=m**3; sc,_,_,_=_build_score(m); evals=[0]
def f(x):
evals[0]+=1
return float(sc([int(round(xi))%6 for xi in x]))
x0=np.array([random.randrange(6) for _ in range(n)],dtype=float)
try:
res=minimize(f,x0,method="Nelder-Mead",
options={"maxiter":min(10000,n*50),"xatol":0.5,"fatol":0.5})
best=[int(round(xi))%6 for xi in res.x]; bs=sc(best)
except Exception as e:
bs=999; r.note=str(e)
r.time_ms=(time.perf_counter()-t0)*1000; r.iters=evals[0]
if bs==0:
sm={}; idx=0
for i in range(m):
for j in range(m):
for k_ in range(m):
sm[(i,j,k_)]=tuple(_ALL_P3[best[idx]]); idx+=1
r.correct=verify_sigma(sm,m); r.proof_type="constructive"
else:
r.proof_type="none"
r.note=f"best_score={bs} β€” continuous opt fails on discrete"
return r
SOLVERS = [solver_v2, solver_A4_level_enum, solver_A3_v1,
solver_A2_backtrack, solver_A1_SA, solver_A0_random, solver_A5_scipy]
# ── Benchmark runner ──────────────────────────────────────────────────────────
def run_benchmark(problems: List[Tuple[int,int]], verbose=True) -> Dict:
all_results = {}
for m,k in problems:
row = {}
if verbose:
print(f"\n {W_}Problem m={m} k={k} ({m**3} vertices):{Z_}")
print(f" {'Solver':<22} {'βœ“':>2} {'Time':>9} {'Proof':<18} {'Iters':>10}")
print(f" {'─'*65}")
for fn in SOLVERS:
try: res=fn(m) if fn!=solver_A3_v1 else fn(m,k)
except Exception as e: res=BResult(fn.__name__,m,note=str(e))
row[fn.__name__]=res
if verbose: print(f" {res.row()}")
all_results[(m,k)]=row
return all_results
def print_summary(all_results, problems):
print(f"\n{'═'*72}")
print(f"{W_}AGGREGATED RESULTS{Z_}")
print('─'*72)
solver_names=[fn.__name__ for fn in SOLVERS]
n=len(problems)
print(f"\n {'Solver':<22} {'Correct':>8} {'Proved-':>8} {'Avg ms':>9} {'Timeouts':>9}")
print(f" {'─'*65}")
for sn in solver_names:
col_res=[all_results[p][sn] for p in problems if p in all_results and sn in all_results[p]]
nc=sum(1 for r in col_res if r.correct)
ni=sum(1 for r in col_res if r.proof_type=="impossible" and r.correct)
nt=sum(1 for r in col_res if r.timed_out)
times=[r.time_ms for r in col_res if not r.timed_out]
avg=sum(times)/len(times) if times else 9999
cc=G_ if nc==n else (Y_ if nc>n//2 else R_)
print(f" {sn:<22} {cc}{nc:>4}/{n:<3}{Z_} {ni:>8} {avg:>9.1f} {nt:>9}")
# Speedup
print(f"\n {W_}Speedup of v2 over alternatives (geometric mean over solved):{Z_}")
v2_name="solver_v2"
for sn in solver_names:
if sn==v2_name: continue
ratios=[]
for p in problems:
if p not in all_results: continue
v2=all_results[p].get(v2_name); alt=all_results[p].get(sn)
if v2 and alt and v2.correct and alt.time_ms>0 and v2.time_ms>0:
ratios.append(alt.time_ms/v2.time_ms)
if not ratios: continue
geo=math.exp(sum(math.log(x) for x in ratios)/len(ratios))
col=G_ if geo>2 else Y_
bar="β–“"*min(int(math.log2(max(geo,1))*4),40)
print(f" {sn:<22} {col}{geo:>8.1f}Γ—{Z_} {bar}")
def w4_benchmark():
print(f"\n{'═'*72}")
print(f"{W_}W4 CORRECTION: O(m^m) β†’ O(1){Z_}")
print('─'*72)
print(f"\n {'m':>4} {'v1 W4 (wrong)':>14} {'v2 W4=phi(m)':>13} "
f"{'v1 ms':>8} {'v2 ms':>8} {'speedup':>10}")
print(f" {'─'*65}")
for m in [3,4,5,6,7,8,9,10]:
phi=sum(1 for r in range(1,m) if gcd(r,m)==1)
t1=time.perf_counter()
if m<=7: v1=sum(1 for b in iprod(range(m),repeat=m) if gcd(sum(b)%m,m)==1)//m
else: v1=None
v1_ms=(time.perf_counter()-t1)*1000
t2=time.perf_counter(); v2=phi; v2_ms=(time.perf_counter()-t2)*1000
sp=f"{v1_ms/max(v2_ms,1e-9):.0f}Γ—" if v1 else "∞"
v1_str=str(v1) if v1 else "DNF(>10s)"
print(f" {m:>4} {v1_str:>14} {v2:>13} "
f"{v1_ms:>7.2f} {v2_ms:>7.4f} {sp:>10}")
print(f"\n {G_}v1 W4 was wrong by up to 16,807Γ—. v2 W4=phi(m) is exact.{Z_}")
def main():
args=sys.argv[1:]
quick='--quick' in args
if '--w4' in args:
w4_benchmark(); return
if '--scaling' in args:
print(f"\n{W_}Scaling analysis{Z_}")
for m in [3,4,5,6,7]:
for fn in [solver_v2, solver_A2_backtrack]:
r=fn(m)
sym=f"{G_}βœ“{Z_}" if r.correct else (f"{Y_}T{Z_}" if r.timed_out else f"{R_}βœ—{Z_}")
print(f" m={m} {fn.__name__:<22}: {sym} {r.time_ms:.1f}ms")
return
problems = [(3,3),(4,3),(5,3)] if quick else [(3,3),(4,3),(4,4),(5,3),(6,3)]
print('═'*72)
print(f"{W_}BENCHMARK β€” v2.0 vs Alternatives{Z_}")
print(f"{D_}Timeout: {TIMEOUT}s per solver per problem{Z_}")
print('═'*72)
w4_benchmark()
all_results=run_benchmark(problems,verbose=True)
print_summary(all_results,problems)
if __name__ == "__main__":
main()