Promotes the validated shape-curve DP (experiments/shapecurve_spike.py, 2g7.4, DESIGN.md §37.2) from a reference-only spike into src/homemaker_layout/shapecurve.py, and wires it into driver._evaluate as a warm-start for innerloop.optimise: when eligible (single storey, no leaf_sharing/superpose/max_share/multi_use) and no caller-supplied x0, the DP's exact shape-feasible ratio point is written onto the tree before NM runs, off by default (shapecurve_warmstart=/--shapecurve-warmstart). Caught and fixed a latent bug promoting the spike: realise() could leave numpy.float64 in `division`, which yaml.safe_dump can't serialise — the original spike never round-tripped through dom.dumps so this was never hit. A/B on harbor-house-l0 (experiments/ab_shapecurve_warmstart.py, budget=2000, 5 seeds): mean total fails 16.6 (on) vs 19.6 (off), ~3.5x mean fitness improvement; mean hard-fail count alone was a noise-level wash at this sample size. Full writeup in DESIGN.md §37.4. Deliberately deferred to new tracked beads (children of 2g7): DP-exact hard pre-filter (wkh), multi-storey below-link support (koo), leaf_sharing/ co_type modelling (tym), true skew-quad polygon algebra (ekc) — 6xh stays in_progress pending those. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LSwQwpEaHFBkeVSDDWd75S
70 lines
2.8 KiB
Python
70 lines
2.8 KiB
Python
"""A/B: does shapecurve-DP NM warm-start beat today's cold/proportion-aware
|
|
start on wall-clock/evals-to-fail-count, on the real ``driver.search`` loop
|
|
(homemaker-py-6xh, DESIGN.md §37.4)?
|
|
|
|
Scoped to the DP's validated envelope (DESIGN.md §37.2): single storey, no
|
|
leaf_sharing/superpose/max_share/multi_use. ``examples/harbor-house-l0`` is
|
|
the single-storey de-risk variant of harbor-house built for exactly this
|
|
purpose (storey_minimum=1) -- the full multi-storey ``examples/harbor-house``
|
|
is out of scope until the multi-storey DP follow-up lands.
|
|
|
|
Metric: mean (n_hard, n_soft, fitness) of ``driver.search``'s best individual
|
|
at a FIXED budget, across several seeds -- same format as §37.1's tiered-
|
|
comparator A/B table, not an evals-to-zero race (harbor-house-l0's small
|
|
programme does not reliably reach 0 hard fails within a script-scale budget
|
|
across all seeds, so a fixed-budget comparison is the fair, reproducible one).
|
|
|
|
Usage: python experiments/ab_shapecurve_warmstart.py [budget] [n_seeds]
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
|
|
from homemaker_layout import dom, driver
|
|
|
|
PROGRAMME_DIR = Path(__file__).parent.parent / "examples" / "harbor-house-l0"
|
|
|
|
|
|
def run_arm(seed_root: dom.Node, budget: int, seed: int, warmstart: bool):
|
|
t0 = time.perf_counter()
|
|
r = driver.search(
|
|
seed_root, PROGRAMME_DIR, budget=budget, pop_size=8, child_budget=80,
|
|
seed_budget=200, seed=seed, leaf_sharing=False,
|
|
shapecurve_warmstart=warmstart,
|
|
)
|
|
elapsed = time.perf_counter() - t0
|
|
return r, elapsed
|
|
|
|
|
|
def main() -> None:
|
|
budget = int(sys.argv[1]) if len(sys.argv) > 1 else 4000
|
|
n_seeds = int(sys.argv[2]) if len(sys.argv) > 2 else 8
|
|
|
|
seed_root = dom.load(str(PROGRAMME_DIR / "init.dom"))
|
|
|
|
rows = []
|
|
for seed in range(n_seeds):
|
|
off, t_off = run_arm(seed_root, budget, seed, warmstart=False)
|
|
on, t_on = run_arm(seed_root, budget, seed, warmstart=True)
|
|
rows.append((seed, off.best.n_hard, off.best.n_soft, off.best.fitness, t_off,
|
|
on.best.n_hard, on.best.n_soft, on.best.fitness, t_on))
|
|
print(f"seed {seed}: off hard={off.best.n_hard} soft={off.best.n_soft} "
|
|
f"fit={off.best.fitness:.4g} {t_off:.1f}s | "
|
|
f"on hard={on.best.n_hard} soft={on.best.n_soft} "
|
|
f"fit={on.best.fitness:.4g} {t_on:.1f}s", flush=True)
|
|
|
|
n = len(rows)
|
|
mean = lambda idx: sum(r[idx] for r in rows) / n
|
|
print()
|
|
print(f"budget={budget} n_seeds={n_seeds} programme={PROGRAMME_DIR}")
|
|
print(f"OFF: mean hard={mean(1):.3f} soft={mean(2):.3f} fitness={mean(3):.6g} "
|
|
f"wall={mean(4):.1f}s")
|
|
print(f"ON : mean hard={mean(5):.3f} soft={mean(6):.3f} fitness={mean(7):.6g} "
|
|
f"wall={mean(8):.1f}s")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|