"""Standalone evaluator for all baseline solutions. Runs each solution directly (no subprocess) and applies the same validation/scoring logic as the evaluate_ori.py files. """ import sys import os import time import importlib.util import numpy as np from scipy.signal import fftconvolve BASEDIR = os.path.dirname(os.path.abspath(__file__)) RESULTS = os.path.join(BASEDIR, "results") def load_module(path, name): spec = importlib.util.spec_from_file_location(name, path) mod = importlib.util.module_from_spec(spec) sys.modules[name] = mod spec.loader.exec_module(mod) return mod # ── Circle Packing ────────────────────────────────────────────────── def eval_circle_packing(path): name = os.path.splitext(os.path.basename(path))[0] mod = load_module(path, f"cp_{name}") t0 = time.time() centers, radii, reported_sum = mod.run_packing() elapsed = time.time() - t0 centers = np.asarray(centers) radii = np.asarray(radii) assert centers.shape == (26, 2), f"centers shape {centers.shape}" assert radii.shape == (26,), f"radii shape {radii.shape}" assert np.all(radii >= 0), "negative radii" actual_sum = float(np.sum(radii)) assert np.isclose(actual_sum, reported_sum, atol=1e-6), \ f"sum mismatch: actual={actual_sum}, reported={reported_sum}" # boundary check for i in range(26): x, y, r = centers[i, 0], centers[i, 1], radii[i] assert x - r >= -1e-6 and x + r <= 1 + 1e-6, f"circle {i} outside x" assert y - r >= -1e-6 and y + r <= 1 + 1e-6, f"circle {i} outside y" # overlap check for i in range(26): for j in range(i + 1, 26): dist = np.sqrt(np.sum((centers[i] - centers[j]) ** 2)) assert dist >= radii[i] + radii[j] - 1e-6, \ f"circles {i}&{j} overlap: dist={dist:.6f}, sum_r={radii[i]+radii[j]:.6f}" return {"combined_score": actual_sum, "elapsed": elapsed} # ── Erdos Min Overlap ─────────────────────────────────────────────── def eval_erdos(path): name = os.path.splitext(os.path.basename(path))[0] mod = load_module(path, f"erdos_{name}") t0 = time.time() h_values, c5_bound, n_points = mod.run(seed=42, budget_s=10.0) elapsed = time.time() - t0 h_values = np.asarray(h_values, dtype=np.float64) n_points = int(n_points) assert h_values.ndim == 1 and h_values.shape[0] == n_points assert np.all(np.isfinite(h_values)) # normalize if needed target = n_points / 2.0 cur = float(np.sum(h_values)) if abs(cur - target) > 1e-8: h_values = h_values * (target / cur) assert np.all(h_values >= -1e-10) and np.all(h_values <= 1 + 1e-10), \ f"h out of [0,1]: [{h_values.min()}, {h_values.max()}]" dx = 2.0 / n_points correlation = np.correlate(h_values, 1.0 - h_values, mode="full") * dx computed_c5 = float(np.max(correlation)) assert np.isclose(computed_c5, float(c5_bound), atol=1e-4), \ f"C5 mismatch: computed={computed_c5:.8f}, reported={float(c5_bound):.8f}" return {"combined_score": -computed_c5, "c5": computed_c5, "elapsed": elapsed} # ── AC1 ───────────────────────────────────────────────────────────── def eval_ac1(path): name = os.path.splitext(os.path.basename(path))[0] mod = load_module(path, f"ac1_{name}") t0 = time.time() seq = mod.run(seed=42, budget_s=10.0) elapsed = time.time() - t0 assert isinstance(seq, list) and len(seq) > 0 a = np.array([float(x) for x in seq]) a = np.clip(a, 0.0, 1000.0) n = len(a) s = float(np.sum(a)) assert s >= 0.01, f"sum too small: {s}" # Use FFT for large sequences if n > 500: conv = fftconvolve(a, a) else: conv = np.convolve(a, a) max_b = float(np.max(conv)) value = 2.0 * n * max_b / (s * s) return {"combined_score": -value, "ac1_value": value, "n": n, "elapsed": elapsed} # ── Main ──────────────────────────────────────────────────────────── if __name__ == "__main__": tasks = { "circle_packing": { "eval_fn": eval_circle_packing, "files": ["thetaevolve.py", "autoevolve.py"], }, "erdos_min_overlap": { "eval_fn": eval_erdos, "files": ["ttt_discover.py", "autoevolve.py"], }, "alphaevolve_ac": { "eval_fn": eval_ac1, "files": ["ttt_discover.py", "alphaevolve_v2.py", "autoevolve.py"], }, } for task_name, cfg in tasks.items(): print(f"\n{'='*60}") print(f" {task_name}") print(f"{'='*60}") for fname in cfg["files"]: fpath = os.path.join(RESULTS, task_name, fname) if not os.path.exists(fpath): print(f" {fname}: FILE NOT FOUND") continue try: result = cfg["eval_fn"](fpath) parts = [f"{k}={v:.8f}" if isinstance(v, float) else f"{k}={v}" for k, v in result.items()] print(f" {fname}: {', '.join(parts)}") except Exception as e: print(f" {fname}: ERROR - {e}")