homemaker-layout/tests/test_collapse_insearch.py
Claude 4180a9eb88
Replace the stale 15-fail acceptance target with the cold-start baseline
DESIGN.md 38.7 pinned Phase 9's acceptance test to "harbor-house reaches its
known 15-fail floor". That figure was measured before 39.4, against harbor's
*effective* programme of 32 instances -- cr1/of/st1/st2 were being read as
generic circulation and silently dropped. Against the real 37-instance
programme the same artefact scores 89, so the target is not measurable; and
2v1, the fix it was the acceptance test for, closed NULL (39.8), so there is
no combined fix left to accept.

New 39.12 records:

* the five evolved-3M*.dom artefacts rescored under the current objective
  (69/85/87/89/145), and why they are not a floor -- they were evolved under
  one objective and are scored under another;
* that the bead's migration premise is stale: experiments/migrate_ju3_rename.py
  does not exist, because ju3 tightened the matching rule at source (39.3)
  instead, so the old artefacts parse correctly with no migration;
* the 4x3-seed 500k cold-start baseline (~430 h) as the reference from here,
  with each programme's sd and its minimum detectable difference at n=3 --
  harbor mean 39.3, sd 5.5, MDD 13.7;
* zero missing-space fails in all twelve runs: the dominant term in the 3M
  artefacts is not one the live search still fails on;
* crinkliness at 112 of the 321 corpus fails (35%), all soft, all in the
  regime 9gj says quality_uncrinkliness returns a flat 0.0 for -- the largest
  single component of the residual is one the objective cannot descend;
* 66 of the 84 hard fails as one access-topology family (not-adjacent-to,
  inaccessible usable space, not connected), mechanism in 39.9.

38.7's acceptance paragraph is annotated in place rather than rewritten. The
connectivity clause is demoted to a separately tracked standing defect: it
appears in 10 of the 12 baseline runs.

tests/test_collapse_insearch.py carried its own stale "82 -> 58"; the same
layout now scores 89 -> 64, so the docstring dates the figure instead of
asserting a current one.

Closes homemaker-py-ut5.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-09-04 17:16:10 +00:00

145 lines
6 KiB
Python

"""Tests for the IN-SEARCH global collapse (homemaker-py-qpk, DESIGN.md §17
follow-on): running Fitness.collapse_global inside every fitness eval instead
of once at finish time.
Covers:
- default-OFF guarantee + conf-driven knobs (adjacency, iters)
- _evaluate_full wiring: collapse_global is invoked (with the right kwargs)
when the flag is on, never when it is off
- end-to-end effect on a real evolved layout, cross-checked against the
documented 94g finish-time result (DESIGN.md §17: 15 -> 12 fails on
evolved-3M-nols-3.dom)
"""
import copy
from pathlib import Path
from unittest.mock import patch
import pytest
from _helpers import with_usage
from homemaker_layout import dom as dom_mod
from homemaker_layout.dom import Node, _link_subtree
from homemaker_layout.fitness import Fitness, load_config
HARBOR = Path(__file__).parent.parent / "examples" / "harbor-house"
def _two_leaf_root(t_left: str, t_right: str, side: float = 6.0, div: float = 0.4):
from homemaker_layout import geometry
geometry.clear_cache()
root = Node(
node=[[0, 0], [side, 0], [side, side], [0, side]],
rotation=0, division=[div, div],
left=Node(type=t_left), right=Node(type=t_right),
)
_link_subtree(root, None, "")
return root
# --------------------------------------------------------------------------- #
# Defaults + conf-driven knobs
# --------------------------------------------------------------------------- #
def test_collapse_insearch_default_off():
fit = Fitness()
assert fit._collapse_insearch is False
assert fit._collapse_insearch_adjacency is True
assert fit._collapse_insearch_iters == 3
def test_collapse_insearch_flag_on():
fit = Fitness(conf={"collapse_insearch": True})
assert fit._collapse_insearch is True
def test_collapse_insearch_adjacency_knob_off():
fit = Fitness(conf={"collapse_insearch": True,
"collapse_insearch_adjacency": False})
assert fit._collapse_insearch_adjacency is False
def test_collapse_insearch_iters_knob():
fit = Fitness(conf={"collapse_insearch": True, "collapse_insearch_iters": 5})
assert fit._collapse_insearch_iters == 5
# --------------------------------------------------------------------------- #
# _evaluate_full wiring
# --------------------------------------------------------------------------- #
def test_evaluate_full_calls_collapse_global_when_on():
fit = Fitness(conf={"collapse_insearch": True, "collapse_insearch_iters": 2,
"spaces": with_usage({"b1": {"size": [16.0, 4.0], "count": 2}})})
root = _two_leaf_root("b1", "b1")
with patch.object(Fitness, "collapse_global", wraps=fit.collapse_global) as m:
fit.score_with_fails(root)
m.assert_called_once()
_, kw = m.call_args
assert kw["adjacency"] is True
assert kw["objective"] == "threshold"
assert kw["preserve_public_access"] is True
assert kw["iters"] == 2
def test_evaluate_full_does_not_call_collapse_global_when_off():
fit = Fitness(conf={"spaces": with_usage({"b1": {"size": [16.0, 4.0], "count": 2}})})
root = _two_leaf_root("b1", "b1")
with patch.object(Fitness, "collapse_global", wraps=fit.collapse_global) as m:
fit.score_with_fails(root)
m.assert_not_called()
# --------------------------------------------------------------------------- #
# End-to-end: matches the documented 94g finish-time result
# --------------------------------------------------------------------------- #
@pytest.mark.skipif(not HARBOR.is_dir(), reason="harbor-house example absent")
def test_collapse_insearch_matches_finish_time_collapse():
"""in-search collapse must reach the SAME layout as finish-time collapse.
That equality is the actual guarantee: `collapse_insearch` runs the same
`collapse_global` earlier in the same pipeline (before the Phase-1 checks
rather than after the whole search), so on a FIXED geometry the two must
agree. Both sides are computed here rather than hard-coded.
This test previously asserted the §17 constants directly -- `15` fails
before collapse and `12` after. Those were measured before §39.4, when
harbor's effective programme was silently 32 instances because codes like
`cr1` were being read as generic circulation; immediately after §39.4 the
same layout scored 82 -> 58, and it has moved again since. Pinning the
endpoints made a live invariant fail whenever the
programme or the objective legitimately changed, while not actually
checking the invariant at all (two independent constants can both drift and
still be equal, or both hold and mask an inequality). At the time of
writing the same layout scores 89 -> 64; DESIGN.md §39.12 restates the
reference figure itself and says why no constant belongs here.
"""
conf, cost = load_config(HARBOR)
conf_ci, _ = load_config(HARBOR, overrides={"collapse_insearch": True})
root = dom_mod.load(str(HARBOR / "evolved-3M-nols-3.dom"))
_, f_base = Fitness(conf, cost).score_with_fails(copy.deepcopy(root))
_, f_ci = Fitness(conf_ci, cost).score_with_fails(copy.deepcopy(root))
# the same collapse, applied once at finish time, scored canonically
finished = copy.deepcopy(root)
Fitness(conf, cost).collapse_global(
finished, adjacency=True, objective="threshold",
preserve_public_access=True, iters=3)
_, f_finish = Fitness(conf, cost).score_with_fails(finished)
assert len(f_ci) == len(f_finish), (
"in-search collapse diverged from finish-time collapse on fixed geometry")
assert len(f_ci) < len(f_base), "collapse must not make the layout worse"
@pytest.mark.skipif(not HARBOR.is_dir(), reason="harbor-house example absent")
def test_collapse_insearch_off_reproduces_baseline():
conf, cost = load_config(HARBOR)
fit = Fitness(conf, cost)
root = dom_mod.load(str(HARBOR / "evolved-3M-nols-3.dom"))
s1, f1 = fit.score_with_fails(copy.deepcopy(root))
s2, f2 = Fitness(conf, cost).score_with_fails(copy.deepcopy(root))
assert s1 == pytest.approx(s2)
assert f1 == f2