Remove two dead paths from the objective

39.24's sweep listed two entries as DEAD rather than suspect -- inert code that
reads as live. Neither changes a score or a failure on any corpus artefact, and
that is verified rather than asserted: every artefact scores identically to its
39.25 measurement.

ratio_public_outside and ratio_private_outside. evaluate_building read both and
multiplied a gaussian into the building factor for each. Neither key exists in
CONF_DEFAULTS and no patterns.config in the repository declares either, so both
branches were guarded and never ran. Removing them also retires what fed them:
the four public_length_*/private_length_* tracking keys accumulated per leaf in
process_storey, and the _public_length/_private_length helpers, which had no
other caller.

NOT removed, because they are live: _public_access, _public_access_outside,
_public_access_pins and the has_public_access_* tracking flags, which drive real
checks and collapse_global's preserve_public_access. Only the length-ratio
machinery was dead.

The daylight quality factor. evaluate_leaf set factors["daylight"] = 1.0
unconditionally -- pinned since the URB_NO_OCCLUSION descope (6) and unable to
be anything else. It was never in _GRADED_FACTORS, so it contributed nothing to
the graded signal, and 39.18's geometric mean then had to special-case it in
factor_is_asked as a factor that is never asked. A constant that exists only to
be excluded is worth deleting. If 2g5 rebuilds occlusion it reintroduces a real
daylight factor, which would need factor_is_asked to say True anyway.

Two tests referenced the removed factor. test_leaf_grade_ignores_non_graded_keys
now names a key that genuinely does not exist; the aggregate underflow test
dropped its daylight entry, which would otherwise have been counted as asked and
changed the expected geometric mean.

Worth doing despite changing no number: 39.20 and 39.25 were both cases where
something inert looked live -- a parity test that never ran, a per-level rule
switched off in every config -- and in both the misreading cost real time and
produced a wrong conclusion. An objective with fewer things in it that do
nothing is one where "this term does nothing" is informative rather than
routine.

Still open on dpt, each needing a ruling or a rate change rather than a
measurement: quality_size's upper side, the minimum-internal-area factor as a
third statement of "build the rooms", and the 0.5**n_fails curve.

426 passed.

Refs homemaker-py-dpt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
This commit is contained in:
Claude 2026-09-06 18:11:09 +00:00
parent 73324deff1
commit 99c85ec4ee
No known key found for this signature in database
5 changed files with 50 additions and 72 deletions

View file

@ -8126,3 +8126,47 @@ where the fraction penalty lifted and fall where a new hard fail landed:
| maple-court | 51→**52**, 65, 52 | 50% (new fail), +1%, +0.4% | | maple-court | 51→**52**, 65, 52 | 50% (new fail), +1%, +0.4% |
| health-centre | 3/9/5 unchanged | +29% … +156% | | health-centre | 3/9/5 unchanged | +29% … +156% |
| programme-house | 1→**2** all seeds | 43%, +24%, 50% | | programme-house | 1→**2** all seeds | 43%, +24%, 50% |
### 39.26 Two dead paths in the objective (`homemaker-py-dpt`)
§39.24's sweep listed two entries as DEAD rather than suspect — inert code that
reads as live. Both are removed here. Neither changes a score or a failure on
any corpus artefact; that is what "dead" meant, and it is verified rather than
asserted: every artefact scores identically to its §39.25 measurement.
**`ratio_public_outside` and `ratio_private_outside`.** `evaluate_building`
read both and multiplied a gaussian into the building factor for each. Neither
key exists in `CONF_DEFAULTS`, and no `patterns.config` in the repository
declares either, so both branches were guarded by `if conf_po and isinstance(...)`
and never ran. Removing them also retires what fed them: the four
`public_length_*` / `private_length_*` tracking keys accumulated per leaf in
`process_storey`, and the `_public_length` / `_private_length` helpers, which
had no other caller.
Note what is **not** removed: `_public_access`, `_public_access_outside` and
`_public_access_pins`, and the `has_public_access_inside` / `..._outside`
tracking flags. Those are live — they drive real checks and
`collapse_global`'s `preserve_public_access`. Only the *length ratio* machinery
was dead.
**The `daylight` quality factor.** `evaluate_leaf` set `factors["daylight"] =
1.0` unconditionally, a factor that has been pinned since the URB_NO_OCCLUSION
descope (§6) and can never be anything else. It was never in `_GRADED_FACTORS`,
so it contributed nothing to the graded signal; §39.18's geometric mean then had
to special-case it in `factor_is_asked` as a factor that is never asked. A
constant that exists only to be excluded is worth deleting.
If the occlusion subsystem is ever rebuilt (`homemaker-py-2g5`), it reintroduces
a real daylight factor; nothing here forecloses that, and a real one would need
`factor_is_asked` to say `True` for it anyway.
**Why this is worth doing at all**, given neither changes a number: §39.20 and
§39.25 were both cases where something inert looked live — a parity test that
never ran, a per-level rule switched off in every config — and in both the
misreading cost real time and produced a wrong conclusion. An objective with
fewer things in it that do nothing is an objective where "this term does
nothing" is informative rather than routine.
Still open on `dpt`, and all three need a ruling or a rate change rather than a
measurement: `quality_size`'s upper side, the minimum-internal-area factor as a
third statement of "build the rooms", and the `0.5 ** n_fails` curve.

View file

@ -1,7 +1,7 @@
"""Native port of Urb's programme-driven fitness: leaf quality terms + cost model. """Native port of Urb's programme-driven fitness: leaf quality terms + cost model.
Scope (homemaker-py-gnw): per-leaf quality factors (perpendicular, proportion, Scope (homemaker-py-gnw): per-leaf quality factors (perpendicular, proportion,
size, width, crinkliness, daylight, access), the programme-driven parameter size, width, crinkliness, access), the programme-driven parameter
lookup chain (``get_space_params``), value rates, and the cost denominator lookup chain (``get_space_params``), value rates, and the cost denominator
(per-leaf area costs, interior/exterior wall edge costs, boundary costs). (per-leaf area costs, interior/exterior wall edge costs, boundary costs).
Storey/building checks, staircases, failure stacking and final assembly are Storey/building checks, staircases, failure stacking and final assembly are
@ -1544,9 +1544,6 @@ class Fitness:
factors["crinkliness"] = f factors["crinkliness"] = f
quality *= f quality *= f
# Daylight pinned to 1 — URB_NO_OCCLUSION semantics (DESIGN.md §6).
factors["daylight"] = 1.0
if len(self.access(leaf, G)) > 0: if len(self.access(leaf, G)) > 0:
f = 1.0 f = 1.0
elif not dom_mod.level_of(leaf) and dom_mod.is_outside(leaf): elif not dom_mod.level_of(leaf) and dom_mod.is_outside(leaf):
@ -1570,8 +1567,6 @@ class Fitness:
`tests/test_fitness_aggregate.py` asserts the invariant this duplication `tests/test_fitness_aggregate.py` asserts the invariant this duplication
rests on: whenever this returns False, the factor really is 1.0. rests on: whenever this returns False, the factor really is 1.0.
""" """
if name == "daylight":
return False # pinned to 1.0, URB_NO_OCCLUSION §6
if name == "size": if name == "size":
return _generic_class(leaf) not in ("o", "s") return _generic_class(leaf) not in ("o", "s")
if name == "crinkliness": if name == "crinkliness":
@ -1923,38 +1918,6 @@ class Fitness:
return True return True
return False return False
def _public_length(self, leaf: Node, root: Node) -> float:
"""Non-private external boundary metres; mirrors ``Urb::Dom::Public_Length``."""
if dom_mod.level_of(leaf) != 0:
return 0.0
total = 0.0
for edge in range(4):
bid = geometry.boundary_id(leaf, edge)
if bid not in frozenset("abcd"):
continue
if self._perimeter_type(root, bid).lower() == "private":
continue
total += geometry.edge_length(leaf, edge)
return total
def _private_length(self, leaf: Node, root: Node) -> float:
"""Private external boundary metres; mirrors ``Urb::Dom::Private_Length``."""
if dom_mod.level_of(leaf) != 0:
return 0.0
total = 0.0
for edge in range(4):
bid = geometry.boundary_id(leaf, edge)
if bid not in frozenset("abcd"):
continue
if self._perimeter_type(root, bid).lower() != "private":
continue
total += geometry.edge_length(leaf, edge)
return total
# ----------------------------------------------------------------------- #
# Extended process_storey (adds circ, stair, tracking)
# ----------------------------------------------------------------------- #
def process_storey( def process_storey(
self, self,
level_root: Node, level_root: Node,
@ -2040,15 +2003,6 @@ class Fitness:
and self._public_access(leaf, root) is not None): and self._public_access(leaf, root) is not None):
tracking["has_public_access_inside"] = True tracking["has_public_access_inside"] = True
pub = self._public_length(leaf, root)
tracking["public_length_all"] = tracking.get("public_length_all", 0.0) + pub
if dom_mod.is_outside(leaf):
tracking["public_length_outside"] = tracking.get("public_length_outside", 0.0) + pub
priv = self._private_length(leaf, root)
tracking["private_length_all"] = tracking.get("private_length_all", 0.0) + priv
if dom_mod.is_outside(leaf):
tracking["private_length_outside"] = tracking.get("private_length_outside", 0.0) + priv
for a, b in G.edges(): for a, b in G.edges():
cost += self.edge_cost(G, a, b, fail) cost += self.edge_cost(G, a, b, fail)
for leaf in level_root.leaves(): for leaf in level_root.leaves():
@ -2096,19 +2050,6 @@ class Fitness:
f2 = gaussian(actual_internal, 1.0, min_required, min_required * 0.15) f2 = gaussian(actual_internal, 1.0, min_required, min_required * 0.15)
factor *= f2 factor *= f2
# Public/private ratios (optional config)
pub_all = tracking.get("public_length_all", 0.0)
pub_ratio = tracking.get("public_length_outside", 0.0) / pub_all if pub_all else 0.0
conf_po = self.conf("ratio_public_outside")
if conf_po and isinstance(conf_po, list):
factor *= gaussian(pub_ratio, 1.0, conf_po[0], conf_po[1])
priv_all = tracking.get("private_length_all", 0.0)
priv_ratio = tracking.get("private_length_outside", 0.0) / priv_all if priv_all else 0.0
conf_pr = self.conf("ratio_private_outside")
if conf_pr and isinstance(conf_pr, list):
factor *= gaussian(priv_ratio, 1.0, conf_pr[0], conf_pr[1])
# Staircase volume (multi-level only) # Staircase volume (multi-level only)
lvls = dom_mod.levels(root) lvls = dom_mod.levels(root)
if len(lvls) > 1: if len(lvls) > 1:
@ -2188,10 +2129,6 @@ class Fitness:
tracking: dict = { tracking: dict = {
"has_public_access_outside": False, "has_public_access_outside": False,
"has_public_access_inside": False, "has_public_access_inside": False,
"public_length_all": 0.0,
"public_length_outside": 0.0,
"private_length_all": 0.0,
"private_length_outside": 0.0,
"stair_fit": [], "stair_fit": [],
"_failures": failures, "_failures": failures,
} }

View file

@ -76,10 +76,6 @@ def _native_evaluate(src: Path):
tracking: dict = { tracking: dict = {
"has_public_access_outside": False, "has_public_access_outside": False,
"has_public_access_inside": False, "has_public_access_inside": False,
"public_length_all": 0.0,
"public_length_outside": 0.0,
"private_length_all": 0.0,
"private_length_outside": 0.0,
"stair_fit": [], "stair_fit": [],
"_failures": failures, "_failures": failures,
} }

View file

@ -335,8 +335,10 @@ def test_leaf_grade_sums_over_failing_factors():
def test_leaf_grade_ignores_non_graded_keys(): def test_leaf_grade_ignores_non_graded_keys():
# daylight is pinned and never a graded factor even if below threshold. # Only _GRADED_FACTORS contribute; anything else is ignored however low.
assert _leaf_grade({"daylight": 0.0}) == 0.0 # (This used to name "daylight", a factor pinned to 1.0 since the
# URB_NO_OCCLUSION descope and removed entirely in §39.26.)
assert _leaf_grade({"not_a_factor": 0.0}) == 0.0
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #

View file

@ -87,7 +87,7 @@ def test_geometric_mean_is_the_product_when_every_factor_is_asked():
fit = Fitness(*load_config(EXAMPLES / "harbor-house")) fit = Fitness(*load_config(EXAMPLES / "harbor-house"))
leaf = dom_mod.Node(type="r") leaf = dom_mod.Node(type="r")
factors = {"perpendicular": 0.9, "proportion": 0.8, "size": 0.5, factors = {"perpendicular": 0.9, "proportion": 0.8, "size": 0.5,
"width": 0.95, "crinkliness": 0.4, "access": 1.0, "daylight": 1.0} "width": 0.95, "crinkliness": 0.4, "access": 1.0}
asked = [v for k, v in factors.items() if fit.factor_is_asked(k, leaf)] asked = [v for k, v in factors.items() if fit.factor_is_asked(k, leaf)]
expected = math.prod(asked) ** (1.0 / len(asked)) expected = math.prod(asked) ** (1.0 / len(asked))
assert fit._aggregate_geometric(leaf, factors) == pytest.approx(expected) assert fit._aggregate_geometric(leaf, factors) == pytest.approx(expected)
@ -111,7 +111,6 @@ def test_it_does_not_underflow_where_the_product_would():
tiny = 1e-60 tiny = 1e-60
factors = {k: tiny for k in ("perpendicular", "proportion", "size", factors = {k: tiny for k in ("perpendicular", "proportion", "size",
"width", "crinkliness", "access")} "width", "crinkliness", "access")}
factors["daylight"] = 1.0
assert math.prod(factors[k] for k in factors) == 0.0 # product underflows assert math.prod(factors[k] for k in factors) == 0.0 # product underflows
assert fit._aggregate_geometric(leaf, factors) == pytest.approx(tiny, rel=1e-6) assert fit._aggregate_geometric(leaf, factors) == pytest.approx(tiny, rel=1e-6)