homemaker-layout/tests/test_driver.py
Claude d0567d7a74
Remove the Perl oracle
Owner's decision: "we need to abandon the perl oracle, this was only useful
when initially porting, but I suspect many of the remaining problems have been
carried in from the perl (such as the weird scoring of outdoor and circulation
space, which definitely needs fixing)".

39 supports that second clause. Every defect the section found is inherited,
not introduced: the two-sided crinkliness gaussian that double-charges surplus
daylight (39.14), quality as a product over a variable number of factors
(39.18), value_supported priced as value_inside so a terrace was worth more per
m2 than a room (39.19), and circulation returning 0.07 per unit cost (hxi).

So parity with the oracle was never a safety net -- it was a commitment to
reproduce those defects. Each of 39.14, 39.18 and 39.19 would have been a
parity failure had parity ever been checked, and keeping the tests would have
meant reverting the fixes or explaining the failures away.

Removed: oracle.py, test_oracle.py, the two parity tests and their fixture
machinery in test_dom_corpus.py, innerloop.OracleEvaluator with its use_native
and urb_root plumbing, the same plumbing through driver, and fourteen
experiments/ scripts that could only run against Perl. Several of those are
cited in earlier DESIGN sections; the citations now point into git history,
which is the honest state -- they had been unrunnable since the oracle root
(/home/bruno/src/urb) stopped being present. run_search is superseded by
run_search_scaled, which does the same job natively.

Kept: dump_areas.pl/.py, which validate GEOMETRY against Urb (4.1) rather than
fitness, and the prose in fitness_cmd.py and dom.py explaining why the
.score/.fails formats are shaped as they are. Provenance is worth keeping; a
dead code path is not.

CLAUDE.md updated: fitness.py is the only evaluator, and "Urb did it this way"
is no longer an argument that a constant is right. 39.16 is the standing
counterweight in the other direction -- the crinkliness target WAS right and
twice looked wrong only because the code reading it was misunderstood.
Inheritance is neither evidence for nor against.

410 passed. The 69 removed cases account exactly: 64 parity (all skipped, since
no oracle .score was ever committed), 4 in test_oracle.py, and the guard test
39.20 added as a stopgap.

Closes homemaker-py-118. Files homemaker-py-bk9 for the re-baseline that 39.19
made necessary.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-09-06 07:56:24 +00:00

746 lines
34 KiB
Python

"""Driver tests with a faked inner loop (no oracle, no perl)."""
import copy
from pathlib import Path
import numpy as np
import pytest
from homemaker_layout import dom, driver, fitness, innerloop, solver
CORPUS = Path(__file__).parent.parent / "examples" / "programme-house"
SEED_FILE = CORPUS / "c964435454c459f86c3ed9a5a7621132.dom"
INIT_FILE = CORPUS / "init.dom"
pytestmark = pytest.mark.skipif(not CORPUS.is_dir(), reason="Corpus not available")
def test_free_with_keys_aligns_with_free_branches():
for f in sorted(CORPUS.glob("*.dom")):
root = dom.load(str(f))
assert [b for _, b in innerloop.free_with_keys(root)] == solver.free_branches(root), f.name
@pytest.fixture
def fake_inner(monkeypatch):
"""Deterministic fake fitness: rewards leaf count up to 12; consumes the
full budget; applies a recognisable ratio so Lamarckian write-back is
observable."""
calls = []
def fake_optimise(root, programme_dir, x0=None, budget=200, **kw):
n_leaves = sum(len(lvl.leaves()) for lvl in dom.levels(root))
fitness = 1.0 / (1.0 + abs(12 - n_leaves)) + 1e-6 * len(calls)
calls.append({"budget": budget, "x0": x0, "kw": kw})
for _, b in innerloop.free_with_keys(root):
b.division = [0.25, 0.25]
return innerloop.Result(
x=np.array([0.25]), fitness=fitness, n_fails=0, fail_lines=(),
x0_fitness=fitness / 2, x0_n_fails=1, n_evals=budget, n_oracle_calls=1,
)
monkeypatch.setattr(innerloop, "optimise", fake_optimise)
return calls
def test_search_respects_budget_and_logs(fake_inner):
seed_root = dom.load(str(SEED_FILE))
lines = []
r = driver.search(seed_root, CORPUS, budget=1000, pop_size=4,
child_budget=80, seed_budget=120, seed=1, log=lines.append)
# budget accounting: seed (120) + children (80 each), stop at >= 1000
assert r.n_evals >= 1000
assert r.n_evals == 120 + 80 * ((r.n_evals - 120) // 80)
assert r.n_evals - 1000 < 80
assert r.n_topologies == 1 + (r.n_evals - 120) // 80
assert lines, "improvements must be logged"
# history monotone in evals and fitness
evs = [h[0] for h in r.history]
fits = [h[1] for h in r.history]
assert evs == sorted(evs)
assert fits == sorted(fits)
assert r.best.fitness == max(fits)
assert len(r.population) <= 4
# Lamarckian write-back observable in the best individual
assert all(b.division == [0.25, 0.25] for _, b in innerloop.free_with_keys(r.best.root))
def test_search_children_warm_start_and_local_sigma(fake_inner):
seed_root = dom.load(str(SEED_FILE))
driver.search(seed_root, CORPUS, budget=500, pop_size=4,
child_budget=60, seed_budget=100, seed=0)
seed_call, child_calls = fake_inner[0], fake_inner[1:]
assert seed_call["x0"] is None and seed_call["budget"] == 100
assert child_calls
for c in child_calls:
assert c["budget"] == 60
assert c["x0"] is not None # warm-started
# inherited cuts carry the parent's written-back ratios
assert np.isin(c["x0"], [0.25, 0.5]).all()
assert "sigmas" not in c["kw"] # NM inner loop takes no sigmas
def test_best_root_dumps_valid_dom(fake_inner, tmp_path):
seed_root = dom.load(str(SEED_FILE))
r = driver.search(seed_root, CORPUS, budget=400, pop_size=3,
child_budget=60, seed_budget=100, seed=2)
out = tmp_path / "best.dom"
dom.dump(r.best.root, str(out))
reloaded = dom.load(str(out))
assert sum(len(lvl.leaves()) for lvl in dom.levels(reloaded)) == \
sum(len(lvl.leaves()) for lvl in dom.levels(r.best.root))
def test_bootstrap_cold_start(fake_inner):
"""Bootstrap auto-triggers from a bare undivided plot and fills the
population with pop_size diverse random topologies before the main loop."""
init_root = dom.load(str(INIT_FILE))
assert not init_root.divided, "init.dom should be an undivided bare plot"
pop_size = 4
child_budget = 60
budget = 500
r = driver.search(init_root, CORPUS, budget=budget, pop_size=pop_size,
child_budget=child_budget, seed_budget=100, seed=7)
# All evaluations use child_budget (no seed_budget call)
assert r.n_evals % child_budget == 0
assert r.n_evals >= budget
assert r.n_evals - budget < child_budget
# Every topology (bootstrap + main loop) is counted
assert r.n_topologies == r.n_evals // child_budget
# Population is full
assert len(r.population) == pop_size
# Bootstrap individuals all had x0=None (cold starts)
assert all(c["x0"] is None for c in fake_inner[:pop_size])
# Bootstrap uses exploratory sigma schedule (inner_kw={}, no sigmas override)
assert all("sigmas" not in c["kw"] for c in fake_inner[:pop_size])
# Main loop children are warm-started
main_calls = fake_inner[pop_size:]
assert main_calls # at least one main-loop child
assert all(c["x0"] is not None for c in main_calls)
def test_bootstrap_disabled_for_divided_seed(fake_inner):
"""A divided seed (warm start) auto-selects the legacy single-seed path."""
seed_root = dom.load(str(SEED_FILE))
assert seed_root.divided
r = driver.search(seed_root, CORPUS, budget=500, pop_size=4,
child_budget=60, seed_budget=100, seed=0)
# First call is the seed evaluated at seed_budget
assert fake_inner[0]["budget"] == 100
assert fake_inner[0]["x0"] is None
# Remaining are warm-started children at child_budget
assert all(c["budget"] == 60 for c in fake_inner[1:])
def test_random_topology_leaf_count():
"""random_topology produces a topology with at least n_leaves leaves."""
import numpy as np
init_root = dom.load(str(INIT_FILE))
rng = np.random.default_rng(0)
types = ["b1", "b2", "l1", "t1", "t2", "t3", "C", "O"]
for n in (3, 5, 7, 10):
topo = driver.random_topology(init_root, n, rng, types)
n_leaves = sum(len(lvl.leaves()) for lvl in dom.levels(topo))
assert n_leaves >= n
assert n_leaves <= n + 1 # mutate_divide adds exactly one leaf per call
def test_niche_by_signature_keeps_distinct_topologies(fake_inner):
"""§11.5: niching admits at most one individual per topology signature, so
the population is structurally distinct and diversity is reported."""
from homemaker_layout import genome
init_root = dom.load(str(INIT_FILE))
r = driver.search(init_root, CORPUS, budget=2000, pop_size=6,
child_budget=60, seed=3, niche_by_signature=True)
sigs = [genome.signature(p.root) for p in r.population]
assert len(sigs) == len(set(sigs)), "population must be one-per-topology"
assert r.n_distinct_signatures >= len(r.population)
assert r.diversity_history # recorded on each improvement
def test_restart_keeps_elite_and_counts(monkeypatch):
"""§11.5: a stagnation restart fires, is counted, and preserves the best."""
# Saturating fake (no monotone tiebreaker, unlike `fake_inner`): fitness
# peaks at 12 leaves and plateaus, so the best stalls and restarts trigger.
def fake_optimise(root, programme_dir, x0=None, budget=200, **kw):
n_leaves = sum(len(lvl.leaves()) for lvl in dom.levels(root))
fitness = 1.0 / (1.0 + abs(12 - n_leaves))
return innerloop.Result(
x=np.array([0.25]), fitness=fitness, n_fails=0, fail_lines=(),
x0_fitness=fitness / 2, x0_n_fails=1, n_evals=budget, n_oracle_calls=1,
)
monkeypatch.setattr(innerloop, "optimise", fake_optimise)
init_root = dom.load(str(INIT_FILE))
r = driver.search(init_root, CORPUS, budget=4000, pop_size=4,
child_budget=60, seed=5, niche_by_signature=True,
restart_patience=300, restart_elite=1)
assert r.n_restarts >= 1
assert r.best is not None and r.best.fitness > 0
def test_feasibility_filter_off_matches_baseline(fake_inner):
"""§12.3: with the filter and reassociate OFF (defaults), the run is
identical to one that omits the params — a clean A/B control."""
init_root = dom.load(str(INIT_FILE))
base = driver.search(init_root, CORPUS, budget=600, pop_size=4,
child_budget=60, seed_budget=100, seed=9)
off = driver.search(init_root, CORPUS, budget=600, pop_size=4,
child_budget=60, seed_budget=100, seed=9,
enable_reassociate=False, feasibility_filter=False,
feasibility_max_shape_fails=0)
# Same search trajectory: identical best topology and accounting. (Absolute
# fitness carries the fake_inner monotone tiebreaker, which shares one call
# counter across both runs in this fixture, so compare the signature.)
assert off.best.sig == base.best.sig
assert off.n_topologies == base.n_topologies
assert off.n_evals == base.n_evals
def test_enable_shape_repair_threads_fit_into_mutate(fake_inner, monkeypatch):
"""homemaker-py-161: shape_rotate/deslim need a live ``fitness.Fitness`` to
identify failing leaves; ``search`` must only build and pass one when
``enable_shape_repair=True`` — off by default, so ``operators.mutate`` sees
``fit=None`` and (per its own gating) never selects those two operators."""
from homemaker_layout import fitness, operators
seen_fit = []
real_mutate = operators.mutate
def spy_mutate(root, rng, types, **kw):
seen_fit.append(kw.get("fit"))
return real_mutate(root, rng, types, **kw)
monkeypatch.setattr(operators, "mutate", spy_mutate)
init_root = dom.load(str(INIT_FILE))
off = driver.search(init_root, CORPUS, budget=400, pop_size=4,
child_budget=60, seed_budget=100, seed=5)
assert seen_fit and all(f is None for f in seen_fit)
seen_fit.clear()
on = driver.search(init_root, CORPUS, budget=400, pop_size=4,
child_budget=60, seed_budget=100, seed=5,
enable_shape_repair=True)
assert seen_fit and all(isinstance(f, fitness.Fitness) for f in seen_fit)
# NOTE: no bit-identical-trajectory assertion here. Passing a live `fit`
# gives shape_rotate/deslim nonzero weight in operators.mutate's op-choice
# draw, which shifts the RNG mapping for every draw (not just those two
# ops') — same-seed off/on trajectories only coincided by chance for one
# fixed MUTATIONS size, and that coincidence breaks on any addition to
# MUTATIONS (e.g. homemaker-py-8sh's bridge_circulation). The gating
# itself (seen_fit above) is the actual contract under test.
assert off.best.sig and on.best.sig
def test_feasibility_filter_prunes_cheaply(fake_inner, monkeypatch):
"""§12.3 (homemaker-py-9gp.1): a pruned topology costs one feasibility eval
instead of the full child_budget, so the filter explores far more topologies
per budget; pruned individuals never displace the incumbent."""
from homemaker_layout import operators
# Force every filtered child to be pruned (shape-fail floor above any
# threshold and ≥ the incumbent's fail count).
monkeypatch.setattr(operators, "predicted_shape_fails",
lambda root, reqs, fit: 999)
init_root = dom.load(str(INIT_FILE))
budget, child_budget, pop_size = 1200, 60, 4
on = driver.search(init_root, CORPUS, budget=budget, pop_size=pop_size,
child_budget=child_budget, seed_budget=100, seed=4,
feasibility_filter=True, feasibility_max_shape_fails=0)
# Bootstrap (pop_size topologies at child_budget) then 1-eval prunes: the
# remaining budget buys ~one topology per eval, far more than child_budget.
bootstrap_evals = pop_size * child_budget
assert on.n_topologies > pop_size + (budget - bootstrap_evals) // child_budget
assert on.n_evals >= budget
# No pruned (untuned, fitness=0) individual is admitted to the population.
assert all(p.lineage and not p.lineage.startswith("pruned/") for p in on.population)
assert on.best is not None and not on.best.lineage.startswith("pruned/")
def test_shapecurve_warmstart_off_matches_baseline(fake_inner):
"""homemaker-py-6xh: with the flag off (default), the run is identical to
one that omits the param — a clean A/B control, mirroring the existing
feasibility-filter control test."""
init_root = dom.load(str(INIT_FILE))
base = driver.search(init_root, CORPUS, budget=600, pop_size=4,
child_budget=60, seed_budget=100, seed=9)
off = driver.search(init_root, CORPUS, budget=600, pop_size=4,
child_budget=60, seed_budget=100, seed=9,
shapecurve_warmstart=False)
assert off.best.sig == base.best.sig
assert off.n_topologies == base.n_topologies
assert off.n_evals == base.n_evals
def test_shapecurve_warmstart_seeds_ratios_when_eligible(monkeypatch):
"""homemaker-py-6xh: when eligible (single storey, no leaf_sharing/
superpose/max_share/multi_use) and no caller-supplied x0, ``shapecurve.
solve`` is called and its written ratios are on the tree by the time
``innerloop.optimise`` runs — the mechanism the inner loop's own
``x0=None`` (tree's current ratios) picks up as the warm start."""
from homemaker_layout import shapecurve
divisions_at_optimise = []
def fake_optimise(root, programme_dir, x0=None, budget=200, **kw):
divisions_at_optimise.append(
[tuple(b.division) for _, b in innerloop.free_with_keys(root)])
n_leaves = sum(len(lvl.leaves()) for lvl in dom.levels(root))
fit = 1.0 / (1.0 + abs(12 - n_leaves))
for _, b in innerloop.free_with_keys(root):
b.division = [0.25, 0.25]
return innerloop.Result(
x=np.array([0.25]), fitness=fit, n_fails=0, fail_lines=(),
x0_fitness=fit / 2, x0_n_fails=1, n_evals=budget, n_oracle_calls=1,
)
monkeypatch.setattr(innerloop, "optimise", fake_optimise)
solve_calls = []
def spy_solve(root, fit, grid_n=150):
solve_calls.append(len(dom.levels(root)))
for _, b in innerloop.free_with_keys(root):
b.division = [0.37, 0.37]
return True, {}
monkeypatch.setattr(shapecurve, "solve", spy_solve)
# harbor-house-l0 (storey_minimum=1) rather than CORPUS (programme-house,
# storey_minimum=2) — constructive_topology would otherwise grow a
# multi-storey seed and shapecurve.eligible would rightly never fire.
harbor_l0 = Path(__file__).parent.parent / "examples" / "harbor-house-l0"
if not harbor_l0.is_dir():
pytest.skip("harbor-house-l0 not available")
init_root = dom.load(str(harbor_l0 / "init.dom"))
driver.search(init_root, harbor_l0, budget=300, pop_size=2,
child_budget=60, seed_budget=60, seed=3,
shapecurve_warmstart=True, leaf_sharing=False)
assert solve_calls, "shapecurve.solve must be called for eligible children"
assert all(n == 1 for n in solve_calls), "only ever called on single-storey trees"
# the DP-written ratio (0.37) was on the tree when optimise saw it
assert any(
any(abs(t[0] - 0.37) < 1e-9 for t in divs)
for divs in divisions_at_optimise if divs
)
def test_shapecurve_warmstart_handles_multistorey(monkeypatch):
"""homemaker-py-koo: the DP now models ``below``-inherited (wall-stacked)
fixed splits directly (DESIGN.md §37.6), so ``shapecurve.eligible`` no
longer excludes a multi-storey topology and the warm-start path invokes
it exactly as it would a single-storey one."""
from homemaker_layout import shapecurve
def fake_optimise(root, programme_dir, x0=None, budget=200, **kw):
for _, b in innerloop.free_with_keys(root):
b.division = [0.25, 0.25]
return innerloop.Result(
x=np.array([0.25]), fitness=0.5, n_fails=0, fail_lines=(),
x0_fitness=0.25, x0_n_fails=1, n_evals=budget, n_oracle_calls=1,
)
monkeypatch.setattr(innerloop, "optimise", fake_optimise)
solve_calls = []
monkeypatch.setattr(shapecurve, "solve",
lambda root, fit, grid_n=150: (solve_calls.append(1), (True, {}))[1])
multi_root = dom.load(str(SEED_FILE))
assert len(dom.levels(multi_root)) > 1
driver.search(multi_root, CORPUS, budget=200, pop_size=2,
child_budget=60, seed_budget=60, seed=1,
bootstrap=False, shapecurve_warmstart=True, leaf_sharing=False)
assert solve_calls, "shapecurve.solve must be called for an eligible multi-storey child"
HARBOR_L0 = Path(__file__).parent.parent / "examples" / "harbor-house-l0"
def _fake_optimise_ok(root, programme_dir, x0=None, budget=200, **kw):
for _, b in innerloop.free_with_keys(root):
b.division = [0.25, 0.25]
return innerloop.Result(
x=np.array([0.25]), fitness=0.5, n_fails=0, fail_lines=(),
x0_fitness=0.25, x0_n_fails=1, n_evals=budget, n_oracle_calls=1,
)
def test_shapecurve_prune_off_matches_baseline(fake_inner):
"""homemaker-py-wkh: with the flag off (default), the run is identical to
one that omits the param — the same clean A/B control as the existing
feasibility-filter/shapecurve-warmstart control tests."""
init_root = dom.load(str(INIT_FILE))
base = driver.search(init_root, CORPUS, budget=600, pop_size=4,
child_budget=60, seed_budget=100, seed=9)
off = driver.search(init_root, CORPUS, budget=600, pop_size=4,
child_budget=60, seed_budget=100, seed=9,
shapecurve_prune=False)
assert off.best.sig == base.best.sig
assert off.n_topologies == base.n_topologies
assert off.n_evals == base.n_evals
def test_assign_solver_default_matches_greedy(fake_inner):
"""homemaker-py-2g7.5: with assign_solver left at its default ("greedy"),
the run is identical to one that passes it explicitly — the same clean
A/B control as shapecurve_prune's."""
init_root = dom.load(str(INIT_FILE))
base = driver.search(init_root, CORPUS, budget=600, pop_size=4,
child_budget=60, seed_budget=100, seed=9)
explicit = driver.search(init_root, CORPUS, budget=600, pop_size=4,
child_budget=60, seed_budget=100, seed=9,
assign_solver="greedy")
assert explicit.best.sig == base.best.sig
assert explicit.n_topologies == base.n_topologies
assert explicit.n_evals == base.n_evals
def test_enable_reassign_default_off_matches_baseline(fake_inner):
"""homemaker-py-2g7.5: with enable_reassign left at its default (off),
the run is identical to one that passes it explicitly False — the
reassign operator never fires (zero mutation weight)."""
init_root = dom.load(str(INIT_FILE))
base = driver.search(init_root, CORPUS, budget=600, pop_size=4,
child_budget=60, seed_budget=100, seed=9)
off = driver.search(init_root, CORPUS, budget=600, pop_size=4,
child_budget=60, seed_budget=100, seed=9,
enable_reassign=False)
assert off.best.sig == base.best.sig
assert off.n_topologies == base.n_topologies
assert off.n_evals == base.n_evals
def test_shapecurve_prune_vetoes_heuristic_when_dp_feasible(monkeypatch):
"""homemaker-py-wkh (DESIGN.md §37.5): a DP-feasible verdict is a real
certificate that some ratio point clears every leaf's shape threshold, so
it must veto a heuristic-triggered prune outright — even one predicted
from a bad (999-fail) proxy layout — and skip the ``predicted_shape_fails``
eval entirely rather than just override its verdict."""
from homemaker_layout import operators, shapecurve
monkeypatch.setattr(shapecurve, "is_feasible", lambda root, fit, grid_n=150: True)
pred_calls = []
monkeypatch.setattr(operators, "predicted_shape_fails",
lambda root, reqs, fit: pred_calls.append(1) or 999)
monkeypatch.setattr(innerloop, "optimise", _fake_optimise_ok)
if not HARBOR_L0.is_dir():
pytest.skip("harbor-house-l0 not available")
root = dom.load(str(HARBOR_L0 / "init.dom"))
ind, used = driver._evaluate(
root, HARBOR_L0, x0=None, budget=100, inner_kw={}, lineage="child",
feasibility_max_shape_fails=0, best_n_fails=5, leaf_sharing=False,
shapecurve_prune=True)
assert not pred_calls, "heuristic proxy must be skipped when DP proves feasibility"
assert not ind.lineage.startswith("pruned/")
assert used == 100
def test_shapecurve_prune_hard_prunes_when_dp_infeasible_and_incumbent_perfect(monkeypatch):
"""homemaker-py-wkh: DP-infeasible proves the shape-fail floor is >=1
(exact, 0/200 measured false negatives — DESIGN.md §37.2), which alone
beats a zero-total-fail incumbent — an exact prune, no heuristic count
needed."""
from homemaker_layout import operators, shapecurve
monkeypatch.setattr(shapecurve, "is_feasible", lambda root, fit, grid_n=150: False)
pred_calls = []
monkeypatch.setattr(operators, "predicted_shape_fails",
lambda root, reqs, fit: pred_calls.append(1) or 0)
if not HARBOR_L0.is_dir():
pytest.skip("harbor-house-l0 not available")
root = dom.load(str(HARBOR_L0 / "init.dom"))
ind, used = driver._evaluate(
root, HARBOR_L0, x0=None, budget=100, inner_kw={}, lineage="child",
feasibility_max_shape_fails=0, best_n_fails=0, leaf_sharing=False,
shapecurve_prune=True)
assert not pred_calls, "the exact DP verdict makes the heuristic proxy redundant here"
assert ind.lineage.startswith("pruned/")
assert used == 1
def test_shapecurve_prune_defers_to_heuristic_when_incumbent_nonzero(monkeypatch):
"""homemaker-py-wkh: DP-infeasible only proves the shape-fail floor is
>=1, not that it reaches an arbitrary best_n_fails>0, so that case must
still fall through to today's heuristic-count decision unchanged."""
from homemaker_layout import operators, shapecurve
monkeypatch.setattr(shapecurve, "is_feasible", lambda root, fit, grid_n=150: False)
pred_calls = []
monkeypatch.setattr(operators, "predicted_shape_fails",
lambda root, reqs, fit: pred_calls.append(1) or 999)
if not HARBOR_L0.is_dir():
pytest.skip("harbor-house-l0 not available")
root = dom.load(str(HARBOR_L0 / "init.dom"))
ind, used = driver._evaluate(
root, HARBOR_L0, x0=None, budget=100, inner_kw={}, lineage="child",
feasibility_max_shape_fails=0, best_n_fails=5, leaf_sharing=False,
shapecurve_prune=True)
assert pred_calls, "heuristic proxy must still be consulted when best_n_fails>0"
assert ind.lineage.startswith("pruned/")
assert used == 1
def test_search_parallel_smoke():
"""n_workers>1 runs without error and produces valid results."""
init_root = dom.load(str(INIT_FILE))
r = driver.search(init_root, CORPUS, budget=160, pop_size=2,
child_budget=80, seed=0, n_workers=2)
assert r.best is not None
assert r.best.fitness > 0
assert r.n_evals >= 160
assert 1 <= len(r.population) <= 2
assert r.n_topologies >= 2 # at least the bootstrap individuals
@pytest.mark.parametrize("workers", [2, 3, 4])
def test_search_is_reproducible_at_a_fixed_worker_count(workers):
"""Same seed + SAME worker count => byte-identical (homemaker-py-xcy/b8g).
``_run_batch`` used to admit futures in completion order (``as_completed``),
which varies run-to-run; with the order-sensitive ``admit`` (n_evals accrual,
first-of-tie wins ``best``) that made parallel searches non-reproducible.
Admitting in submission order fixed it.
Note the invariant is per worker count, and deliberately so. `n_workers` is
an algorithm parameter: `batch_n = min(n_workers, ...)` children are bred
from one population snapshot before any is admitted, so different worker
counts explore different trajectories from the same seed (§38.17). This
parametrises over several counts to check each is internally stable; it does
NOT assert that they agree with each other, because they legitimately need
not.
"""
def run():
r = driver.search(dom.load(str(INIT_FILE)), CORPUS, budget=1200,
pop_size=8, child_budget=80, seed=0, n_workers=workers)
return (r.best.n_fails, r.best.fitness, r.best.sig, tuple(r.history))
assert run() == run(), (
f"search at n_workers={workers} is not reproducible run-to-run")
def test_scoring_a_frozen_design_is_deterministic():
"""No floating-point/BLAS nondeterminism in a single eval (homemaker-py-b8g).
b8g suspected "a single fitness eval on a fixed genome returning different
fail counts across runs", plausibly BLAS threading. It does not: measured
bit-identical over 20 in-process repeats and 8 processes with different
PYTHONHASHSEED, and pinning OMP/OPENBLAS/MKL to one thread changes nothing.
This guards the floor the reproducibility argument stands on.
"""
import copy
conf, cost = fitness.load_config(CORPUS)
root = dom.load(str(INIT_FILE))
results = {
fitness.Fitness(conf, cost).score_with_fails(copy.deepcopy(root))
for _ in range(8)
}
assert len(results) == 1, "scoring a frozen design is not deterministic"
def _shared_best_result() -> driver.SearchResult:
"""A SearchResult whose best carries a live 3-room shared leaf (share=3),
plus a distinct C leaf — the harbor-house pathology in miniature."""
root = dom.Node(node=[[0, 0], [12, 0], [12, 8], [0, 8]],
height=2.7, wall_outer=0.25, wall_inner=0.08,
rotation=0, division=[0.5, 0.5])
root.left = dom.Node(type="n", share=3, share_type="n")
root.right = dom.Node(type="C")
dom.link(root)
best = driver.Individual(root=root, fitness=1e-5, n_fails=3, ratios={},
lineage="construct/0")
r = driver.SearchResult(best=best, population=[best], n_evals=1000,
n_topologies=5, n_distinct_signatures=4, n_restarts=1)
r.history = [(80, 1e-6, "construct/0"), (160, 1e-5, "core_divide noop")]
return r
def test_polish_finish_unfolds_and_stitches_rescore(fake_inner):
# homemaker-py-3l6, polish_budget<=0: unfold the shared leaf, rescore once
# under leaf_sharing off, and stitch accounting/history onto the sharing run.
r0 = _shared_best_result()
r = driver.polish_finish(r0, CORPUS, polish_budget=0, rescore_budget=150)
# the shared leaf is materialised into 3 distinct n rooms, stamps cleared
leaves = r.best.root.leaves()
assert sum(1 for lf in leaves if lf.type == "n") == 3
assert all(lf.share == 1 for lf in leaves)
# accounting is cumulative (1000 sharing evals + one 150-eval rescore)
assert r.n_evals == 1000 + 150
assert r.n_topologies == 5 + 1
assert r.n_distinct_signatures == 4 + 1
assert r.n_restarts == 1
# history keeps both phases, tagged so the objective change is visible
assert [lin for *_, lin in r.history[:2]] == [
"share:construct/0", "share:core_divide noop"]
assert r.history[-1][2].startswith("polish:")
# the rescore ran with leaf_sharing off (no sharing override reaches the
# inner) but collapse_insearch defaults on (homemaker-py-1ph), so that's
# the only override present
assert fake_inner[-1]["kw"].get("conf_overrides") == {"collapse_insearch": True}
def test_polish_finish_runs_polish_search(fake_inner):
# polish_budget>0: a warm-started no-sharing search runs from the unfolded
# genome and its evals accrue on top of the sharing run.
r0 = _shared_best_result()
r = driver.polish_finish(r0, CORPUS, polish_budget=400, pop_size=3,
child_budget=80, seed=1)
assert r.n_evals > 1000 + 400 - 80 # sharing 1000 + ~400 polish evals
assert r.best.root.leaves() # a valid materialised genome survived
assert sum(1 for lf in r.best.root.leaves() if lf.share > 1) == 0
assert r.history[0][2].startswith("share:")
assert any(lin.startswith("polish:") for *_, lin in r.history)
def test_polish_finish_noop_without_best():
empty = driver.SearchResult(best=None, population=[], n_evals=0, n_topologies=0)
assert driver.polish_finish(empty, CORPUS, polish_budget=100) is empty
def test_search_seed_pop_evaluates_given_population(fake_inner):
# homemaker-py-kpu: seed_pop supplies an explicit initial population; each
# given root is evaluated (not bootstrapped/single-seeded) before the loop.
pop_roots = [dom.load(str(SEED_FILE)) for _ in range(3)]
r = driver.search(dom.load(str(INIT_FILE)), CORPUS, budget=0, pop_size=3,
child_budget=80, seed_budget=100, seed=0, seed_pop=pop_roots)
# budget 0 ⇒ only the 3 seed-pop evals run (100 each), no children
assert r.n_evals == 300
assert r.n_topologies == 3
assert all(ind.lineage.startswith("anneal-seed/") for ind in r.population)
def test_search_annealed_stitches_phases_and_finishes_honest(fake_inner):
# homemaker-py-kpu (Schedule B): the grain ramp runs one phase per ladder
# step plus a de-share polish, with cumulative accounting, a grain-tagged
# history, and a materialised (share-free) honest best.
r = driver.search_annealed(
dom.load(str(INIT_FILE)), CORPUS, budget=600, polish_budget=200,
grain_ladder=(3, 2), pop_size=3, child_budget=80, seed_budget=80, seed=0)
assert r.best is not None
# honest output: every shared leaf is materialised before the polish phase
assert all(lf.share == 1 for lf in r.best.root.leaves())
# accounting is cumulative across both sharing phases + polish
assert r.n_evals >= 600 + 200 - 80
# history is grain-tagged and ordered: first phase g3, then g2, then polish
tags = [lin.split(":", 1)[0] for *_, lin in r.history]
assert tags[0] == "g3"
assert "g2" in tags
assert tags[-1] == "polish"
# eval offsets are monotone non-decreasing across the stitched phases
evs = [e for e, *_ in r.history]
assert evs == sorted(evs)
def test_search_annealed_degenerate_ladder_falls_back(fake_inner):
# A ladder with no grain >= 2 has nothing to anneal: a plain no-sharing search
# over the full budget (+ polish), and the best is honest (share-free).
r = driver.search_annealed(
dom.load(str(INIT_FILE)), CORPUS, budget=300, polish_budget=100,
grain_ladder=(1,), pop_size=3, child_budget=80, seed_budget=80, seed=0)
assert r.best is not None
assert r.n_evals >= 300
assert all(lf.share == 1 for lf in r.best.root.leaves())
def test_use_tiers_prefers_fewer_hard_over_fewer_total_fails(monkeypatch):
"""homemaker-py-2g7.3: with use_tiers=True the outer comparator is
(-n_hard, -n_soft, fitness) instead of (-n_fails, fitness). Construct a
seed (0 hard, 2 soft) vs. a mutated child (1 hard, 0 soft, FEWER total
fails and HIGHER raw fitness) — the flat comparator prefers the child
(1 < 2 total fails); the tiered comparator must keep the seed (0 < 1
hard fails dominates regardless of soft count or fitness)."""
from homemaker_layout import innerloop
seed_root = dom.load(str(SEED_FILE))
calls = [] # first call is always the seed eval; every later call is a child
def fake_optimise(root, programme_dir, x0=None, budget=200, **kw):
for _, b in innerloop.free_with_keys(root):
b.division = [0.25, 0.25]
is_seed = len(calls) == 0
calls.append(1)
if is_seed:
fail_lines = ("0/lr proportion", "0/lr crinkliness") # 0 hard, 2 soft
fit = 0.5
else:
fail_lines = ("level 0 not connected",) # 1 hard, 0 soft
fit = 0.9 # higher raw fitness AND fewer total fails than the seed
return innerloop.Result(
x=np.array([0.25]), fitness=fit, n_fails=len(fail_lines),
fail_lines=fail_lines, x0_fitness=fit, x0_n_fails=len(fail_lines),
n_evals=budget, n_oracle_calls=1,
)
monkeypatch.setattr(innerloop, "optimise", fake_optimise)
common_kw = dict(programme_dir=CORPUS, pop_size=1, seed_budget=50,
child_budget=50, budget=100, bootstrap=False, seed=0)
flat = driver.search(seed_root, **common_kw)
assert flat.best.n_fails == 1 # flat comparator: fewer total fails wins
calls.clear()
tiered = driver.search(copy.deepcopy(seed_root), use_tiers=True, **common_kw)
assert tiered.best.n_hard == 0 # tiered comparator: fewer hard fails wins
assert tiered.best.n_fails == 2
# --------------------------------------------------------------------------- #
# Crash safety for long runs: driver.search's checkpoint hook
# --------------------------------------------------------------------------- #
def test_search_checkpoint_is_called_with_the_current_best(tmp_path):
"""A long search's only output otherwise lands at the very end, so an
abrupt loss takes the whole run with it."""
seen = []
r = driver.search(
dom.load("examples/programme-house/init.dom"),
"examples/programme-house", budget=400, seed=0, child_budget=20,
checkpoint=lambda best, n: seen.append((n, best.n_fails)),
checkpoint_every=50)
assert seen, "checkpoint was never called"
evals = [n for n, _ in seen]
assert evals == sorted(evals)
assert all(b - a >= 50 for a, b in zip(evals, evals[1:])), \
"checkpoint_every must rate-limit by evals, not fire on every improvement"
assert r.best is not None
def test_search_checkpoint_failure_never_kills_the_run():
"""Losing a checkpoint is bad; losing the search because a checkpoint
failed is worse."""
def boom(best, n):
raise OSError("disk full")
r = driver.search(
dom.load("examples/programme-house/init.dom"),
"examples/programme-house", budget=400, seed=0, child_budget=20,
checkpoint=boom, checkpoint_every=50)
assert r.best is not None
def test_search_without_checkpoint_is_unchanged():
"""Default off: no hook, byte-identical result to before the feature."""
kw = dict(budget=400, seed=0, child_budget=20)
a = driver.search(dom.load("examples/programme-house/init.dom"),
"examples/programme-house", **kw)
b = driver.search(dom.load("examples/programme-house/init.dom"),
"examples/programme-house",
checkpoint=None, checkpoint_every=0, **kw)
assert a.best.fitness == b.best.fitness
assert a.n_evals == b.n_evals