2026-06-12 14:22:26 +01:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
"""End-to-end memetic search on programme-house (homemaker-py-b39 acceptance).
|
|
|
|
|
|
|
|
|
|
Runs driver.search from a corpus seed within a stated oracle-evaluation
|
|
|
|
|
budget, logs every improvement, writes the best design to
|
|
|
|
|
scratch/search_best.dom and independently re-scores it through the oracle to
|
|
|
|
|
prove the output is a valid .dom whose recorded fitness is reproducible.
|
|
|
|
|
|
|
|
|
|
The interesting bar: the seed's geometry-only optimum (inner loop alone,
|
|
|
|
|
DESIGN §4.7 reference: c964435 -> 0.00674). Anything above that is value
|
|
|
|
|
added by *topology* search.
|
|
|
|
|
|
|
|
|
|
Run under the go-forward fitness:
|
2026-06-12 22:22:16 +01:00
|
|
|
URB_NO_OCCLUSION=1 python3 experiments/run_search.py [budget] [rng-seed] [seed.dom] [out.dom]
|
2026-06-12 14:22:26 +01:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import math
|
2026-06-13 09:56:01 +01:00
|
|
|
import shutil
|
2026-06-12 14:22:26 +01:00
|
|
|
import sys
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
2026-06-14 08:18:06 +01:00
|
|
|
from homemaker_layout import dom, driver, oracle # noqa: E402
|
2026-06-12 14:22:26 +01:00
|
|
|
|
|
|
|
|
URB = Path("/home/bruno/src/urb")
|
|
|
|
|
EX = URB / "examples" / "programme-house"
|
|
|
|
|
SEED_FILE = EX / "c964435454c459f86c3ed9a5a7621132.dom"
|
|
|
|
|
OUT = Path(__file__).resolve().parents[1] / "scratch" / "search_best.dom"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main() -> int:
|
|
|
|
|
budget = int(sys.argv[1]) if len(sys.argv) > 1 else 2000
|
|
|
|
|
rng_seed = int(sys.argv[2]) if len(sys.argv) > 2 else 0
|
2026-06-12 22:22:16 +01:00
|
|
|
seed_file = Path(sys.argv[3]) if len(sys.argv) > 3 else SEED_FILE
|
|
|
|
|
out = Path(sys.argv[4]) if len(sys.argv) > 4 else OUT
|
|
|
|
|
print(f"seed={seed_file.name} budget={budget} oracle evals, rng seed {rng_seed}",
|
2026-06-12 14:22:26 +01:00
|
|
|
flush=True)
|
|
|
|
|
|
2026-06-12 22:22:16 +01:00
|
|
|
r = driver.search(dom.load(str(seed_file)), EX, budget=budget,
|
2026-06-12 14:22:26 +01:00
|
|
|
urb_root=URB, seed=rng_seed, log=lambda m: print(m, flush=True))
|
|
|
|
|
|
|
|
|
|
print(f"\ndone: {r.n_evals} oracle evals across {r.n_topologies} topologies")
|
|
|
|
|
print(f"best: {r.best.fitness:.6g} (fails {r.best.n_fails}) via {r.best.lineage}")
|
|
|
|
|
print("population: " + ", ".join(f"{p.fitness:.4g}/{p.n_fails}f" for p in r.population))
|
|
|
|
|
|
2026-06-12 22:22:16 +01:00
|
|
|
out.parent.mkdir(parents=True, exist_ok=True)
|
2026-06-13 09:56:01 +01:00
|
|
|
config_src = EX / "patterns.config"
|
|
|
|
|
if config_src.exists() and not (out.parent / "patterns.config").exists():
|
|
|
|
|
shutil.copy(config_src, out.parent)
|
2026-06-12 22:22:16 +01:00
|
|
|
dom.dump(r.best.root, str(out))
|
|
|
|
|
s = oracle.score(out, URB)
|
2026-06-12 14:22:26 +01:00
|
|
|
ok = math.isclose(s.fitness, r.best.fitness, rel_tol=1e-9)
|
2026-06-12 22:22:16 +01:00
|
|
|
print(f"\n{out} re-scored standalone: {s.fitness:.6g} ({s.n_fails} fails) "
|
2026-06-12 14:22:26 +01:00
|
|
|
f"-> {'MATCHES search record' if ok else 'MISMATCH'}")
|
|
|
|
|
return 0 if ok else 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
sys.exit(main())
|