From ef23b7c54c24fa8b485bba9772efe89bc716a37e Mon Sep 17 00:00:00 2001 From: WojtAcht Date: Wed, 14 Oct 2026 17:07:41 +0200 Subject: [PATCH] fix: respect known_best in CMLON global funnel proportion (#41) --- src/lonkit/lon.py | 7 +++---- tests/test_cmlon_metrics.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 tests/test_cmlon_metrics.py diff --git a/src/lonkit/lon.py b/src/lonkit/lon.py index 5fd92e7..2d6a14a 100644 --- a/src/lonkit/lon.py +++ b/src/lonkit/lon.py @@ -623,7 +623,7 @@ def compute_network_metrics(self, known_best: float | None = None) -> dict[str, else: sink_strength = 0.0 - gfunnel = self._compute_global_funnel_proportion() + gfunnel = self._compute_global_funnel_proportion(igs) return { "n_optima": n_optima, @@ -636,9 +636,8 @@ def compute_network_metrics(self, known_best: float | None = None) -> dict[str, "global_funnel_proportion": gfunnel, } - def _compute_global_funnel_proportion(self) -> float: - """Compute proportion of nodes that can reach a global optimum.""" - igs = self.get_global_sinks() + def _compute_global_funnel_proportion(self, igs: list[int]) -> float: + """Compute proportion of nodes that can reach any of the supplied global sinks.""" if not igs: return 0.0 diff --git a/tests/test_cmlon_metrics.py b/tests/test_cmlon_metrics.py new file mode 100644 index 0000000..6a06888 --- /dev/null +++ b/tests/test_cmlon_metrics.py @@ -0,0 +1,29 @@ +"""Regression coverage for CMLON reference optima (issue #41).""" + +import pandas as pd +import pytest + +from lonkit import LON, LONConfig + + +@pytest.mark.parametrize("method", ["compute_network_metrics", "compute_metrics"]) +@pytest.mark.parametrize("minimize", [True, False]) +@pytest.mark.parametrize( + ("known_best", "expected_funnels", "expected_proportion"), + [(0.0, 0, 0.0), (1.0, 1, 1.0), (None, 1, 1.0)], +) +def test_global_funnel_proportion_uses_known_best( + method, minimize, known_best, expected_funnels, expected_proportion +): + sign = 1 if minimize else -1 + trace = pd.DataFrame( + [(1, sign * 2.0, "a", sign * 1.0, "b")], + columns=["run", "fit1", "node1", "fit2", "node2"], + ) + cmlon = LON.from_trace_data(trace, LONConfig(minimize=minimize)).to_cmlon() + reference = sign * known_best if known_best is not None else None + + metrics = getattr(cmlon, method)(known_best=reference) + + assert metrics["n_global_funnels"] == expected_funnels + assert metrics["global_funnel_proportion"] == expected_proportion