homemaker-layout/experiments/refine_sweep.py

80 lines
2.8 KiB
Python
Raw Permalink Normal View History

"""Population sweep: warm-start refine every evolved candidate and tally results.
For each real .dom in the example dir, score it, run the solver as a geometry
optimiser (warm-start, no strip), and re-score. Reports how often bottom-up
sizing improves vs regresses total fitness, plus aggregate fail-count change.
This is a breadth check on the solver-as-optimiser role; raw fitness is still
confounded by the 0.5^n failure cliff and any topological defects, so the
fail-count and per-candidate detail matter as much as the win/loss tally.
"""
import shutil
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from homemaker_layout import dom, oracle, programme, solver # noqa: E402
URB = Path("/home/bruno/src/urb")
EX = URB / "examples/programme-house"
def _is_candidate(p: Path) -> bool:
# real designs: 32-hex hashes or candidate-NNN; skip init and our scratch
name = p.stem
return name not in {"init", "original", "roundtrip", "solved", "refined"}
def main() -> None:
scratch = Path(__file__).resolve().parents[1] / "scratch"
scratch.mkdir(exist_ok=True)
shutil.copy(EX / "patterns.config", scratch / "patterns.config")
targets = programme.load_programme(str(EX / "patterns.config"))
doms = sorted(p for p in EX.glob("*.dom") if _is_candidate(p))
win = loss = tie = 0
fails_before = fails_after = 0
rows = []
for src in doms:
try:
shutil.copy(src, scratch / "orig.dom")
s0 = oracle.score(scratch / "orig.dom", URB)
root = dom.load(str(src))
# gentlest refiner: nudge cut POSITIONS for programme-room area only,
# keep evolved cut angles and leave circulation/shape untouched.
solver.solve_ratios(
root, targets, strip=False, perpendicular=False,
weight_width=0.0, weight_proportion=0.0, min_width_generic=0.0,
)
dom.dump(root, str(scratch / "ref.dom"))
s1 = oracle.score(scratch / "ref.dom", URB)
except Exception as e: # noqa: BLE001
rows.append(f" {src.name:40s} ERROR {e}")
continue
fails_before += s0.n_fails
fails_after += s1.n_fails
if s1.fitness > s0.fitness * 1.001:
win += 1
mark = ""
elif s1.fitness < s0.fitness * 0.999:
loss += 1
mark = ""
else:
tie += 1
mark = "="
rows.append(
f" {mark} {src.name:40s} {s0.fitness:.4g} -> {s1.fitness:.4g}"
f" fails {s0.n_fails}->{s1.n_fails}"
)
print("\n".join(rows))
n = win + loss + tie
print(f"\n{n} candidates: {win} improved, {loss} regressed, {tie} tied")
print(f"total fails: {fails_before} -> {fails_after}")
if __name__ == "__main__":
main()