Owner: "maybe plot_ratio is having unintended consequences, Alexander simply says that all levels should have accessible outside space, he doesn't say how much." Both halves are right, and the codebase had the two rules the wrong way round. ratio_outside is a gaussian on the outdoor FRACTION, applied as a whole-building multiplier, and its declared targets have no stated basis and contradict each other: health-centre targets 0.06 and sits at 0.096-0.129, so it is penalised x0.39-x0.77 for having too MUCH; programme-house targets 0.30 and sits at 0.098-0.293, penalised x0.40-x0.999 for having too LITTLE. Penalties as large as the ratio_circulation ones 39.24 removed, pulling two programmes in opposite directions on the same quantity. force_roof_garden already implements the rule Alexander actually states -- per level, no outdoor space at all is a hard fail, no quantity attached. It has existed all along and was switched OFF in every corpus config. Near-miss worth recording: measuring first, I found zero "no outside space" fails across the twelve baseline runs and briefly read that as the requirement being met everywhere. It meant the check never ran. Same shape as 39.20's parity tests -- no failures from a test that is not executing looks exactly like no failures from a test that passes, and the tell was again the config, not the code. Enabled, it bites on 4 of 25 baseline levels: maple s0 level 1, and programme-house level 0 in all three seeds. A house with no outdoor space on its own ground floor is a fair criticism of the layout, and exactly what a building-level fraction cannot catch, since 22% outdoor concentrated on one storey satisfies it perfectly. The upper side ratio_outside used to provide is covered in a better currency by the minimum-internal-area factor (internal area >= 1.2x the programme's declared room area), which is live -- binding on harbor s0 x0.920 and programme-house s0 x0.787. This is the one change in 39.22-39.25 whose risk is NOT measured: nothing here proves the outdoor fraction will not drift up once the search is free to raise it, and outdoor space is profitable (1.64 return against a room's 0.66). The re-baseline (bk9) is what shows it, and 39.25 asks for the fraction to be recorded there. 4 hard fails added, none removed. 426 passed. Refs homemaker-py-hxi. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
71 lines
3 KiB
Python
71 lines
3 KiB
Python
"""Outdoor space is a per-level requirement, not a fraction (DESIGN.md §39.25).
|
|
|
|
Owner: "Alexander simply says that all levels should have accessible outside
|
|
space, he doesn't say how much."
|
|
|
|
The codebase had both rules and had them the wrong way round: the qualitative
|
|
one Alexander states (`force_roof_garden`, which fails a level with no outdoor
|
|
space at all) was switched OFF in every corpus config, while the quantitative
|
|
one he does not state (`ratio_outside`, a gaussian on the outdoor fraction) was
|
|
switched on with four mutually contradictory targets.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import copy
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from homemaker_layout import dom as dom_mod
|
|
from homemaker_layout.fitness import Fitness, load_config
|
|
|
|
EXAMPLES = Path(__file__).resolve().parent.parent / "examples"
|
|
CORPUS = ["harbor-house", "maple-court", "health-centre", "programme-house"]
|
|
pytestmark = pytest.mark.skipif(not (EXAMPLES / "harbor-house").is_dir(),
|
|
reason="examples absent")
|
|
|
|
|
|
@pytest.mark.parametrize("name", CORPUS)
|
|
def test_the_qualitative_rule_is_on_and_the_quantitative_one_is_off(name):
|
|
conf, _ = load_config(EXAMPLES / name)
|
|
assert conf["force_roof_garden"], (
|
|
"Alexander's requirement -- every level has accessible outside space --"
|
|
" must actually be enforced")
|
|
assert conf["ratio_outside"] is None, (
|
|
"the outdoor FRACTION is not a rule Alexander states (§39.25)")
|
|
|
|
|
|
def test_a_level_with_no_outdoor_space_fails():
|
|
"""The rule has to bite, or turning it on achieved nothing. programme-house
|
|
puts no outdoor space on its ground floor in every baseline seed."""
|
|
d = EXAMPLES / "programme-house"
|
|
seen = 0
|
|
for p in sorted(d.glob("coldstart-500000-s*.dom")):
|
|
conf, cost = load_config(d)
|
|
_, fails = Fitness(conf, cost).score_with_fails(dom_mod.load(str(p)))
|
|
assert any("no outside space" in f for f in fails), p.name
|
|
seen += 1
|
|
assert seen == 3
|
|
|
|
|
|
def test_that_failure_is_hard():
|
|
"""It is a structural provision no ratio-solve can supply, so it must tier
|
|
HARD -- a soft fail would let the search buy it off with shape quality."""
|
|
from homemaker_layout.fitness import classify_fail_tier
|
|
assert classify_fail_tier("level 1 no outside space") == "hard"
|
|
|
|
|
|
def test_disabling_the_fraction_removes_no_failure():
|
|
"""`ratio_outside` was a value multiplier, never a fail source, so the only
|
|
fail-set movement in §39.25 comes from switching the per-level rule ON."""
|
|
for name in CORPUS:
|
|
d = EXAMPLES / name
|
|
for p in sorted(d.glob("coldstart-500000-s*.dom")):
|
|
root = dom_mod.load(str(p))
|
|
conf, cost = load_config(d)
|
|
with_frac, _ = load_config(
|
|
d, overrides={"ratio_outside": [0.15, 0.1]})
|
|
_, f_off = Fitness(conf, cost).score_with_fails(copy.deepcopy(root))
|
|
_, f_on = Fitness(with_frac, cost).score_with_fails(copy.deepcopy(root))
|
|
assert f_off == f_on, f"{p.name}: ratio_outside moved a failure"
|