From e957ba32f97dd4efab3d23b081d88f9ac804b29e Mon Sep 17 00:00:00 2001 From: Uriah Finkel Date: Fri, 21 Aug 2026 14:40:51 +0300 Subject: [PATCH 1/2] Add internal evaluation semantic metadata --- .../processing/evaluation_semantics.py | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 src/rtichoke/processing/evaluation_semantics.py diff --git a/src/rtichoke/processing/evaluation_semantics.py b/src/rtichoke/processing/evaluation_semantics.py new file mode 100644 index 00000000..1929082c --- /dev/null +++ b/src/rtichoke/processing/evaluation_semantics.py @@ -0,0 +1,60 @@ +"""Internal semantic metadata for model/population evaluations. + +This module does not change public inputs or rendered grouping. It records the +semantic information that can be known from the existing input shapes while +preserving ``reference_group`` as the compatibility grouping key. +""" + +from dataclasses import dataclass +from typing import Dict, Mapping, Optional, Union + +import numpy as np + +_SHARED_POPULATION = "__shared_population__" + + +@dataclass(frozen=True) +class _EvaluationMetadata: + """Semantic identity available for one compatibility reference group.""" + + reference_group: str + evaluation: str + model: Optional[str] + population: str + + +def _build_evaluation_metadata( + probs: Mapping[str, np.ndarray], + reals: Union[np.ndarray, Dict[str, np.ndarray]], + times: Union[np.ndarray, Dict[str, np.ndarray]], +) -> dict[str, _EvaluationMetadata]: + """Describe evaluations without changing existing grouping behavior. + + With shared outcome/time arrays, probability keys identify models evaluated + in one shared population. With keyed outcome/time dictionaries, keys identify + distinct evaluation populations, but the current API does not separately + encode model identity; that field is therefore left unknown rather than + inferred from a generic group label. + """ + keyed_population = isinstance(reals, dict) or isinstance(times, dict) + + if keyed_population: + return { + group: _EvaluationMetadata( + reference_group=group, + evaluation=group, + model=None, + population=group, + ) + for group in probs + } + + return { + group: _EvaluationMetadata( + reference_group=group, + evaluation=group, + model=group, + population=_SHARED_POPULATION, + ) + for group in probs + } From b92794c6c7118d14c6daaa49f03dbfcf4b564130 Mon Sep 17 00:00:00 2001 From: Uriah Finkel Date: Fri, 21 Aug 2026 14:41:08 +0300 Subject: [PATCH 2/2] Test internal evaluation semantic metadata --- tests/test_evaluation_semantics.py | 63 ++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 tests/test_evaluation_semantics.py diff --git a/tests/test_evaluation_semantics.py b/tests/test_evaluation_semantics.py new file mode 100644 index 00000000..2b4d432c --- /dev/null +++ b/tests/test_evaluation_semantics.py @@ -0,0 +1,63 @@ +import numpy as np + +from rtichoke.processing.evaluation_semantics import ( + _SHARED_POPULATION, + _build_evaluation_metadata, +) + + +def test_shared_outcomes_identify_models_in_one_population(): + probs = { + "Model A": np.array([0.1, 0.9]), + "Model B": np.array([0.2, 0.8]), + } + reals = np.array([0, 1]) + times = np.array([5.0, 10.0]) + + metadata = _build_evaluation_metadata(probs, reals, times) + + assert set(metadata) == set(probs) + assert metadata["Model A"].reference_group == "Model A" + assert metadata["Model A"].evaluation == "Model A" + assert metadata["Model A"].model == "Model A" + assert metadata["Model A"].population == _SHARED_POPULATION + assert metadata["Model B"].model == "Model B" + assert metadata["Model B"].population == _SHARED_POPULATION + + +def test_keyed_outcomes_identify_populations_without_guessing_model_identity(): + probs = { + "Population A": np.array([0.1, 0.9]), + "Population B": np.array([0.2, 0.8]), + } + reals = { + "Population A": np.array([0, 1]), + "Population B": np.array([1, 0]), + } + times = { + "Population A": np.array([5.0, 10.0]), + "Population B": np.array([4.0, 9.0]), + } + + metadata = _build_evaluation_metadata(probs, reals, times) + + assert metadata["Population A"].reference_group == "Population A" + assert metadata["Population A"].evaluation == "Population A" + assert metadata["Population A"].model is None + assert metadata["Population A"].population == "Population A" + assert metadata["Population B"].model is None + assert metadata["Population B"].population == "Population B" + + +def test_paired_labels_remain_compatibility_evaluation_labels(): + pair = "Model A @ Population A" + probs = {pair: np.array([0.1, 0.9])} + reals = {pair: np.array([0, 1])} + times = {pair: np.array([5.0, 10.0])} + + metadata = _build_evaluation_metadata(probs, reals, times)[pair] + + assert metadata.reference_group == pair + assert metadata.evaluation == pair + assert metadata.population == pair + assert metadata.model is None