homemaker-layout/experiments/run_and_capture_91f.py

75 lines
2.7 KiB
Python
Raw Permalink Normal View History

homemaker-py-91f: residual diagnostic on the current full default stack Re-ran the §13.1/§13.2-style per-leaf fail-breakdown diagnostic on real driver.search_staged runs (budget 20000, seeds 0-2, harbor-house and maple-court) under the current full default stack (leaf-sharing x3, depth-balanced, interior-O, share-aware edge cap) -- never decomposed by category since those defaults were flipped on. Finding: crinkliness (48%) and size (20.6%) now dominate the residual on both programmes (~69% combined); construction-completeness fails (missing space, adjacency, level, connectivity) are down to a small tail (<=6% each). This revises erc.1's old recommendation to deprioritise compactness-cuts in favour of leaf-sharing -- leaf-sharing is now fully deployed and crinkliness is proportionally more dominant than ever, so DESIGN.md §13.11 recommends reopening a compactness/crinkliness-targeted construction lever as the next concrete step. Also files two bugs found while validating the methodology: dumping and reloading a .dom under leaf_sharing+collapse_insearch does not reproduce the search's own in-process fail count (homemaker-py-iio), and run_staged_search.py's own sanity rescore omits the collapse_insearch override (homemaker-py-7ua). experiments/run_and_capture_91f.py sidesteps this by capturing the true in-process fails list instead of rescoring from disk; experiments/diag_residual_91f.py tallies fail categories from those sidecars. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R8agJBT2ZpmF3ErW7wi2wY
2026-08-01 19:38:46 +01:00
#!/usr/bin/env python3
"""homemaker-py-91f support script: run the §13.9 full-default-stack staged
search and capture the TRUE in-process fail list (not a post-hoc rescore).
Investigation finding (see bd issue filed alongside 91f): reloading a
dumped .dom and rescoring it with matching leaf_sharing/collapse_insearch
conf does NOT reliably reproduce driver.search_staged's own reported
n_fails once collapse_insearch's cell-relabelling is doing real work --
scoring `copy.deepcopy(r.best.root)` immediately in-process (before any
dom.dump/dom.load round trip) DOES reliably reproduce it (verified: exact
match across 5 repeats and against the live search's own log). So this
script never rescores from disk -- it captures the fails list right off
the in-memory search result, and only dumps the .dom for reference.
Usage:
python3 experiments/run_and_capture_91f.py <programme> <seed> <outdir>
"""
from __future__ import annotations
import copy
import json
import sys
import time
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from homemaker_layout import dom, driver, fitness # noqa: E402
ROOT = Path(__file__).resolve().parents[1]
OVERRIDES = {"leaf_sharing": True, "share_edge_cap": True, "collapse_insearch": True}
def main() -> int:
prog = sys.argv[1]
seed = int(sys.argv[2])
outdir = Path(sys.argv[3])
outdir.mkdir(parents=True, exist_ok=True)
pdir = ROOT / "examples" / prog
seed_root = dom.load(str(pdir / "init.dom"))
t0 = time.perf_counter()
r = driver.search_staged(
seed_root, pdir, budget=20000, pop_size=16, child_budget=80,
seed_budget=300, stage1_frac=0.4, base_p=0.15, p_crossover=0.2,
seed=seed, n_workers=1,
leaf_sharing=True, leaf_share_factor=3,
depth_balanced=True, interior_outside=True, outside_divisor=3,
)
elapsed = time.perf_counter() - t0
conf, cost = fitness.load_config(pdir, overrides=OVERRIDES)
fit = fitness.Fitness(conf, cost)
score, fails = fit.score_with_fails(copy.deepcopy(r.best.root))
match = (len(fails) == r.best.n_fails)
print(f"{prog} seed={seed}: elapsed={elapsed:.1f}s search_n_fails="
f"{r.best.n_fails} in_process_rescore_n_fails={len(fails)} "
f"match={match}", file=sys.stderr)
dom.dump(r.best.root, str(outdir / f"{prog}_s{seed}.dom"))
with open(outdir / f"{prog}_s{seed}.fails.json", "w") as fh:
json.dump({
"programme": prog, "seed": seed, "elapsed_s": elapsed,
"search_n_fails": r.best.n_fails, "search_fitness": r.best.fitness,
"rescore_n_fails": len(fails), "rescore_match": match,
"fails": fails,
}, fh, indent=2)
return 0 if match else 1
if __name__ == "__main__":
sys.exit(main())