From c70f6730a3a613ae3e517045081f8078883bb723 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Thu, 9 Jul 2026 13:19:22 +0000 Subject: [PATCH] =?UTF-8?q?Remove=20the=20superseded=20role-1=20impacts=20?= =?UTF-8?q?scorer;=20signals=20carry=20the=20objective=20currency=20?= =?UTF-8?q?=F0=9F=9F=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- domain/modelling/optimisation/optimiser.py | 5 ++-- domain/modelling/scoring/scoring.py | 32 ---------------------- tests/domain/modelling/test_scoring.py | 12 ++++---- 3 files changed, 9 insertions(+), 40 deletions(-) diff --git a/domain/modelling/optimisation/optimiser.py b/domain/modelling/optimisation/optimiser.py index 57976c4ca..a40db733c 100644 --- a/domain/modelling/optimisation/optimiser.py +++ b/domain/modelling/optimisation/optimiser.py @@ -33,8 +33,9 @@ from domain.modelling.simulation import EpcSimulation @dataclass(frozen=True) class ScoredOption: """A candidate Measure Option paired with its role-1 (independent-vs- - baseline) SAP gain — the optimiser's input signal. Cost is read from the - Option; the gain is supplied by scoring.""" + baseline) gain in the goal objective's currency — SAP points by default, + kg CO2 / £ saved for the goal-aligned Scenarios (ADR-0062). Cost is read + from the Option; the gain is supplied by scoring.""" option: MeasureOption sap_gain: float diff --git a/domain/modelling/scoring/scoring.py b/domain/modelling/scoring/scoring.py index 8ac0701c9..5145385b8 100644 --- a/domain/modelling/scoring/scoring.py +++ b/domain/modelling/scoring/scoring.py @@ -83,38 +83,6 @@ def marginal_impacts( return marginals_from_scores(cascade_scores(scorer, baseline, overlays)) -def independent_option_impacts( - scorer: PackageScorer, - baseline: EpcPropertyData, - options: Sequence[MeasureOption], -) -> list[MeasureImpact]: - """Score each Option's overlay independently against the baseline (role 1 — - the optimiser's approximate input signal). Each *distinct* Simulation Overlay - is scored once (Options sharing an overlay reuse the result), so the baseline - is scored once plus one score per distinct overlay. Results follow the input - order. These figures are an approximate signal — never surface them as a - measure's true impact.""" - base: Score = scorer.score(baseline, []) - scored: list[tuple[EpcSimulation, MeasureImpact]] = [] - impacts: list[MeasureImpact] = [] - for option in options: - cached = next( - (impact for overlay, impact in scored if overlay == option.overlay), None - ) - if cached is None: - current: Score = scorer.score(baseline, [option.overlay]) - cached = MeasureImpact( - sap_points=current.sap_continuous - base.sap_continuous, - co2_savings_kg_per_yr=base.co2_kg_per_yr - current.co2_kg_per_yr, - energy_savings_kwh_per_yr=( - base.primary_energy_kwh_per_yr - current.primary_energy_kwh_per_yr - ), - ) - scored.append((option.overlay, cached)) - impacts.append(cached) - return impacts - - def independent_option_signals( scorer: PackageScorer, baseline: EpcPropertyData, diff --git a/tests/domain/modelling/test_scoring.py b/tests/domain/modelling/test_scoring.py index 61cfb4544..35541ccc6 100644 --- a/tests/domain/modelling/test_scoring.py +++ b/tests/domain/modelling/test_scoring.py @@ -16,7 +16,7 @@ from domain.modelling.recommendation import MeasureOption from domain.modelling.scoring.scoring import ( MeasureImpact, cascade_scores, - independent_option_impacts, + independent_option_signals, marginal_impacts, marginals_from_scores, ) @@ -64,7 +64,7 @@ def _option(overlay: EpcSimulation) -> MeasureOption: ) -def test_independent_option_impacts_score_each_distinct_overlay_once() -> None: +def test_independent_option_signals_score_each_distinct_overlay_once() -> None: # Arrange baseline: EpcPropertyData = build_epc() scorer = _CountingScorer() @@ -86,15 +86,15 @@ def test_independent_option_impacts_score_each_distinct_overlay_once() -> None: options = [_option(overlay_a), _option(overlay_a_dup), _option(overlay_b)] # Act - impacts: list[MeasureImpact] = independent_option_impacts( - scorer, baseline, options + signals: list[float] = independent_option_signals( + scorer, baseline, options, lambda score: score.sap_continuous ) # Assert # baseline scored once + one score per DISTINCT overlay (a, b) = 3, not 4 assert scorer.calls == 3 - assert impacts[0].sap_points == impacts[1].sap_points == 2.0 - assert impacts[2].sap_points == 3.0 + assert signals[0] == signals[1] == 2.0 + assert signals[2] == 3.0 def test_single_overlay_marginal_is_its_improvement_over_baseline() -> None: