FSO-Genesis-Space / kaggle_search.py
LOOFYYLO's picture
Upload folder using huggingface_hub
2c54126 verified
import math, random, time, sys, os, json
from math import gcd
from itertools import permutations, product as iprod
from typing import Optional, List, Dict, Tuple, Any
# ══════════════════════════════════════════════════════════════════════════════
# UNIFIED ENGINE v2.2 (Basin Escape)
# ══════════════════════════════════════════════════════════════════════════════
def _build_sa(m: int, k: int=3):
n = m**k
arc_s = [[0]*k for _ in range(n)]
m_pow = [m**i for i in range(k)]
m_pow.reverse()
for idx in range(n):
for c in range(k):
if (idx // m_pow[c]) % m == m - 1:
arc_s[idx][c] = idx - (m - 1) * m_pow[c]
else:
arc_s[idx][c] = idx + m_pow[c]
all_p = [list(p) for p in permutations(range(k))]
pa = [[None]*k for _ in range(len(all_p))]
for pi,p in enumerate(all_p):
for at,c in enumerate(p): pa[pi][c] = at
return n, arc_s, pa, all_p
def _sa_score(sigma: List[int], arc_s, pa, n: int, k: int=3) -> int:
total_score = 0
for c in range(k):
vis = bytearray(n); comps = 0
for s in range(n):
if not vis[s]:
comps += 1; cur = s
while not vis[cur]:
vis[cur] = 1; pi = sigma[cur]
cur = arc_s[cur][pa[pi][c]]
total_score += comps - 1
return total_score
def get_node_orbits(m: int, k: int, subgroup_generators: List[Tuple[int, ...]]) -> List[List[int]]:
n = m**k; unvisited = set(range(n)); orbits = []
while unvisited:
start_idx = next(iter(unvisited)); orbit = {start_idx}
queue = [start_idx]; unvisited.remove(start_idx)
while queue:
curr_idx = queue.pop(0)
curr_coords = []; val = curr_idx
for _ in range(k): curr_coords.append(val % m); val //= m
curr_coords.reverse()
for gen in subgroup_generators:
nxt_coords = [(curr_coords[d] + gen[d]) % m for d in range(k)]
nxt_idx = 0
for x in nxt_coords: nxt_idx = nxt_idx * m + x
if nxt_idx in unvisited:
unvisited.remove(nxt_idx); orbit.add(nxt_idx); queue.append(nxt_idx)
orbits.append(list(orbit))
return orbits
def run_hybrid_sa(m: int, k: int=3, seed: int=0, max_iter: int=10_000_000, verbose: bool=True):
n, arc_s, pa, all_p = _build_sa(m, k); nP = len(all_p)
gens = [tuple([m//2]*k)] if m%2==0 else [tuple([1]*k)]
orbits = get_node_orbits(m, k, gens)
rng = random.Random(seed); sigma = [rng.randrange(nP) for _ in range(n)]
cs = _sa_score(sigma, arc_s, pa, n, k); bs = cs; best = sigma[:]
T = 2.0; cool = 0.999998; t0 = time.perf_counter(); stall = 0; reheats = 0
for it in range(max_iter):
if cs == 0: break
if cs <= 12:
# Basin Escape v2.2: greedy descent + orbit swaps
order = list(range(n)); rng.shuffle(order); fixed = False
for v in order:
old = sigma[v]
for pi in rng.sample(range(nP), nP):
if pi == old: continue
sigma[v] = pi; ns = _sa_score(sigma, arc_s, pa, n, k)
if ns < cs:
cs = ns; fixed = True
if cs < bs: bs = cs; best = sigma[:]
else: sigma[v] = old
if fixed: break
if fixed: break
if not fixed:
# Try orbit-flip greedy
for orbit in rng.sample(orbits, min(len(orbits), 20)):
old_vals = [sigma[v] for v in orbit]
for pi in rng.sample(range(nP), nP):
if all(sigma[v] == pi for v in orbit): continue
for v in orbit: sigma[v] = pi
ns = _sa_score(sigma, arc_s, pa, n, k)
if ns < cs:
cs = ns; fixed = True
if cs < bs: bs = cs; best = sigma[:]
else:
for i, v in enumerate(orbit): sigma[v] = old_vals[i]
if fixed: break
if fixed: break
if not fixed:
reheats += 1; stall = 0; sigma = best[:]; cs = bs; T = 1.0
for _ in range(max(1, int(n * 0.03))):
vk = rng.randrange(n); sigma[vk] = rng.randrange(nP)
cs = _sa_score(sigma, arc_s, pa, n, k)
continue
if rng.random() < 0.3:
orbit = rng.choice(orbits); new_p = rng.randrange(nP)
old_vals = [sigma[v] for v in orbit]
for v in orbit: sigma[v] = new_p
ns = _sa_score(sigma, arc_s, pa, n, k); d = ns - cs
if d <= 0 or rng.random() < math.exp(-d / T):
cs = ns
if cs < bs: bs = cs; best = sigma[:]; stall = 0
else: stall += 1
else:
for i, v in enumerate(orbit): sigma[v] = old_vals[i]
stall += 1
else:
v = rng.randrange(n); old = sigma[v]; sigma[v] = rng.randrange(nP)
ns = _sa_score(sigma, arc_s, pa, n, k); d = ns - cs
if d <= 0 or rng.random() < math.exp(-d / T):
cs = ns
if cs < bs: bs = cs; best = sigma[:]; stall = 0
else: stall += 1
else:
sigma[v] = old; stall += 1
if stall > 100_000:
reheats += 1; stall = 0; sigma = best[:]; cs = bs; T = 2.0 / (1.2**reheats)
for _ in range(max(1, int(n * (0.05 if cs > 20 else 0.02)))):
vk = rng.randrange(n); sigma[vk] = rng.randrange(nP)
cs = _sa_score(sigma, arc_s, pa, n, k); continue
T *= cool
if verbose and (it+1) % 100_000 == 0:
print(f" it={it+1:>8,} T={T:.5f} score={cs} best={bs} reh={reheats} {time.perf_counter()-t0:.1f}s")
return best if bs == 0 else None, {"best": bs, "iters": it+1}
def run_fiber_structured_sa(m: int, k: int, seed: int=0, max_iter: int=10_000_000, verbose: bool=True):
n, arc_s, pa, all_p = _build_sa(m, k); nP = len(all_p)
def get_coords(idx):
coords = []; val = idx
for _ in range(k): coords.append(val % m); val //= m
coords.reverse(); return coords
fibers = [sum(get_coords(v)) % m for v in range(n)]
def get_key(v):
c = get_coords(v)
return (fibers[v],) + tuple(c[1:-1])
node_to_key = [get_key(v) for v in range(n)]
keys = list(set(node_to_key))
rng = random.Random(seed); tab = {k_: rng.randrange(nP) for k_ in keys}
def make_sigma(t):
sig = [0]*n
for v in range(n): sig[v] = t[node_to_key[v]]
return sig
sigma = make_sigma(tab); cs = _sa_score(sigma, arc_s, pa, n, k); bs = cs; bt = tab.copy()
T = 2.0; cool = 0.999998; t0 = time.perf_counter(); stall = 0
for it in range(max_iter):
if cs == 0: break
if cs <= 20:
# Basin Escape v2.2 for FiberSA
rk_list = list(keys); rng.shuffle(rk_list); fixed = False
for rk in rk_list:
old = tab[rk]
for pi in rng.sample(range(nP), nP):
if pi == old: continue
tab[rk] = pi; sig = make_sigma(tab)
ns = _sa_score(sig, arc_s, pa, n, k)
if ns < cs:
cs = ns; fixed = True
if cs < bs: bs = cs; bt = tab.copy()
else: tab[rk] = old
if fixed: break
if fixed: break
if not fixed:
tab = bt.copy()
for _ in range(max(1, int(len(keys)*0.05))): tab[rng.choice(keys)] = rng.randrange(nP)
sig = make_sigma(tab); cs = _sa_score(sig, arc_s, pa, n, k)
continue
rk = rng.choice(keys); old = tab[rk]; tab[rk] = rng.randrange(nP)
if tab[rk] == old: continue
sig = make_sigma(tab); ns = _sa_score(sig, arc_s, pa, n, k); d = ns - cs
if d <= 0 or rng.random() < math.exp(-d / T):
cs = ns
if cs < bs: bs = cs; bt = tab.copy(); stall = 0
else: stall += 1
else: tab[rk] = old; stall += 1
if stall > 50_000:
stall = 0; tab = bt.copy(); cs = bs
for _ in range(max(1, int(len(keys)*0.05))): tab[rng.choice(keys)] = rng.randrange(nP)
sig = make_sigma(tab); cs = _sa_score(sig, arc_s, pa, n, k); continue
T *= cool
if verbose and (it+1) % 100_000 == 0:
print(f" it={it+1:>8,} T={T:.5f} score={cs} best={bs} {time.perf_counter()-t0:.1f}s")
sigma_final = make_sigma(bt)
sol_dict = {}
for i, pi in enumerate(sigma_final):
coords = get_coords(i); sol_dict[str(tuple(coords))] = tuple(all_p[pi])
return sigma_final if bs == 0 else None, {"best": bs, "iters": it+1, "solution": sol_dict if bs==0 else None}
# ══════════════════════════════════════════════════════════════════════════════
# MAIN
# ══════════════════════════════════════════════════════════════════════════════
def main():
problem = os.environ.get("KAGGLE_PROBLEM", "P3")
iters = int(os.environ.get("MAX_ITER", 10_000_000))
seed = int(os.environ.get("SEED", 42))
print(f"Problem: {problem}, Max Iters: {iters}, Seed: {seed}")
if problem == "P1":
sol, stats = run_fiber_structured_sa(m=4, k=4, seed=seed, max_iter=iters)
elif problem == "P3":
sol, stats = run_hybrid_sa(m=8, k=3, seed=seed, max_iter=iters)
else:
sol, stats = run_hybrid_sa(m=6, k=3, seed=seed, max_iter=iters)
print(f"\nFinal Stats: {stats}")
if sol:
print("SOLUTION FOUND!")
with open("solution.json", "w") as f:
json.dump(stats.get("solution", sol), f)
if __name__ == "__main__":
main()