homemaker-layout/experiments/accept_innerloop.py

78 lines
3 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""Acceptance run for the geometry inner loop (homemaker-py-1p0).
Gate (DESIGN.md §4.5, issue acceptance criteria): reproduce or exceed the
Nelder-Mead diagnostic gains x1.24 / x1.67 / x1.59, no new failures on
2f45907, candidate-002 and c964435, using the batched compass search.
Usage: accept_innerloop.py [budget] [method] (default: 200 oracle evals, cma)
"""
from __future__ import annotations
import shutil
import sys
import tempfile
import time
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from homemaker import dom, innerloop, oracle # noqa: E402
URB = Path("/home/bruno/src/urb")
EX = URB / "examples" / "programme-house"
# name -> the x-gain the §4.5 Nelder-Mead diagnostic achieved (the bar)
GATE = {
"2f45907abd9accac2a124d311732f749.dom": 1.24,
"candidate-002.dom": 1.67,
"c964435454c459f86c3ed9a5a7621132.dom": 1.59,
}
# The §4.5 bars are single Nelder-Mead draws and the inner loop's per-run
# variance brackets them (candidate-002 drew 0.0117-0.0160 against a 0.0123
# bar). Reproduction within 1% counts as met — decision approved 2026-06-12
# (homemaker-py-1p0); chasing the last fraction with seed rolls would be
# cherry-picking.
NOISE_TOL = 0.99
def main() -> int:
budget = int(sys.argv[1]) if len(sys.argv) > 1 else 200
method = sys.argv[2] if len(sys.argv) > 2 else "cma"
print(f"method={method} budget={budget}")
all_ok = True
with tempfile.TemporaryDirectory(prefix="accept_innerloop_") as tmp:
scratch = Path(tmp)
shutil.copy(EX / "patterns.config", scratch)
for name, bar in GATE.items():
# Baseline = the UNMODIFIED original file, exactly as §4.5 measured
# it. The inner loop's own x0 score is the equal-offset *projection*
# of the original (a==b forced, clipped), which is lower for legacy
# designs with unequal cuts — gains must not be measured from there.
s_orig = oracle.score(shutil.copy(EX / name, scratch), URB)
root = dom.load(str(EX / name))
t0 = time.perf_counter()
r = innerloop.optimise(root, EX, budget=budget, method=method, urb_root=URB)
dt = time.perf_counter() - t0
gain = r.fitness / s_orig.fitness if s_orig.fitness else float("inf")
ok = gain >= bar * NOISE_TOL and r.n_fails <= s_orig.n_fails
all_ok &= ok
print(
f"{name:42s} dof={len(r.x):2d} "
f"orig={s_orig.fitness:.6g}(fails {s_orig.n_fails}) "
f"projected x0={r.x0_fitness:.6g}(fails {r.x0_n_fails}) "
f"opt={r.fitness:.6g}(fails {r.n_fails}) "
f"x{gain:.2f} (bar x{bar:.2f}) "
f"{r.n_evals} evals / {r.n_oracle_calls} oracle calls / {dt:.0f}s "
f"{'PASS' if ok else 'FAIL'}",
flush=True,
)
print("\nGATE " + ("PASSED" if all_ok else "FAILED"))
return 0 if all_ok else 1
if __name__ == "__main__":
sys.exit(main())