Size each constructive-seed cut from leaf TARGET areas (division=[f,f] gives left area-fraction f) and pick each cut's rotation for child squareness — both derived from target dims, topology/type assignment untouched. Area-only regressed (slivers); rotation choice is what makes it pay. End-to-end (20000 evals, 3 seeds, staged): harbor 85.3->74.0 (-13%, best 69), maple-court 151.7->136.0 (-10%, best 126). PROP=0 reproduces the §11.7/§12.1 baselines exactly. programme-house regresses at fixed budget (deeper local optimum walls off the undivide restructuring path) but a budget sweep shows it's convergence speed, not a worse asymptote (PROP=1 reaches 1 fail at 150k). Default-on (seed_proportion_aware=True, env PROP=1). cq1: n_storeys now honours storey_minimum, not just level: keys — programme-house (storey_minimum:2, all rooms level:0) was seeded one storey short and fell through to plain search. New programme.storey_minimum()/n_storeys_for(); driver.search passes min_storeys to the seeder; search_staged routes on the max. No-op for harbor/maple; programme-house single-stage 8.0->5.0. New maple-court best (126) saved as generated.dom. 204 tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
83 lines
3 KiB
Python
83 lines
3 KiB
Python
#!/usr/bin/env python3
|
|
"""Raw-seed fail breakdown: proportion-aware split sizing A/B (homemaker-py-leu.2).
|
|
|
|
Builds the *constructive* seed (operators.constructive_topology, single-stage,
|
|
adjacency-aware) with proportion-aware split sizing OFF then ON, scores each raw
|
|
seed with native fitness BEFORE the inner loop, and tallies fails by family. The
|
|
claim under test (DESIGN.md §12.2): sizing cuts from target areas drops the
|
|
geometry family (size/width/proportion/crinkliness) at the raw seed without
|
|
regressing the adjacency/access family that §11.6/§11.7 fixed.
|
|
|
|
Usage:
|
|
URB_NO_OCCLUSION=1 python3 experiments/measure_seed_geometry.py <programme_dir> [n_seeds]
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import copy
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import numpy as np
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
|
from homemaker_layout import dom, fitness, operators, programme # noqa: E402
|
|
|
|
GEOM = ("perpendicular", "proportion", "size", "width", "crinkliness")
|
|
|
|
|
|
def _categorise(fails: tuple[str, ...]) -> dict[str, int]:
|
|
cats = {"geometry": 0, "access_adj": 0, "missing": 0, "other": 0}
|
|
for f in fails:
|
|
if f.endswith(GEOM) or "edge too long" in f:
|
|
cats["geometry"] += 1
|
|
elif "access" in f or "inaccessible" in f or "adjacent" in f or "adjacency" in f:
|
|
cats["access_adj"] += 1
|
|
elif "missing" in f:
|
|
cats["missing"] += 1
|
|
else:
|
|
cats["other"] += 1
|
|
return cats
|
|
|
|
|
|
def main() -> int:
|
|
programme_dir = Path(sys.argv[1])
|
|
n_seeds = int(sys.argv[2]) if len(sys.argv) > 2 else 10
|
|
|
|
reqs = programme.load_programme_dir(programme_dir)
|
|
types = sorted(reqs) + ["C", "O"]
|
|
conf, cost = fitness.load_config(programme_dir)
|
|
fit = fitness.Fitness(conf, cost)
|
|
|
|
seed_file = programme_dir / "init.dom"
|
|
seed_root = dom.load(str(seed_file))
|
|
|
|
print(f"programme : {programme_dir.name} ({len(reqs)} codes)")
|
|
print(f"seeds : {n_seeds} (single-stage constructive, adjacency-aware)\n")
|
|
|
|
agg = {0: {"total": 0, "geometry": 0, "access_adj": 0, "missing": 0, "other": 0},
|
|
1: {"total": 0, "geometry": 0, "access_adj": 0, "missing": 0, "other": 0}}
|
|
|
|
for s in range(n_seeds):
|
|
for prop in (False, True):
|
|
rng = np.random.default_rng(s)
|
|
topo = operators.constructive_topology(
|
|
seed_root, reqs, rng, types,
|
|
adjacency_aware=True, proportion_aware=prop)
|
|
_score, fails = fit.score_with_fails(copy.deepcopy(topo))
|
|
cats = _categorise(fails)
|
|
agg[int(prop)]["total"] += len(fails)
|
|
for k, v in cats.items():
|
|
agg[int(prop)][k] += v
|
|
|
|
def _avg(d, k):
|
|
return d[k] / n_seeds
|
|
|
|
print(f"{'family':<12} {'PROP=0 (before)':>16} {'PROP=1 (after)':>16}")
|
|
for k in ("geometry", "access_adj", "missing", "other", "total"):
|
|
print(f"{k:<12} {_avg(agg[0], k):>16.1f} {_avg(agg[1], k):>16.1f}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|