Skip to content

k4bench.regression.lineage

k4bench.regression.lineage

Which platforms and detector configs replace which.

Regression history is scoped to (detector, platform, sample): results are filed per detector config and platform, and each is an identity, not a label for "the current one". Two platforms — or two geometry versions — benchmarked side by side are therefore independent series, and need nothing from this module.

A platform that replaces another names it here, and so does a detector config that replaces another (a geometry version bump). The successor's series then continue the predecessor's: the predecessor's nights come first in the history the engine walks, as ordinary baseline points, so a shift caused by the switch is judged like any other step — watch, then regression, with a window bounded by the predecessor's last night — and the baseline re-anchors onto the new level. The successor keeps its own directory, metadata, provenance and report.

A replaced platform or detector is not expected to run once its successor has: its absence from a report is silence rather than a missing-run failure, and it sorts after the ones still running.

Succession is one hop and chains are not allowed: a successor continues its predecessor's own runs only, so one that replaced another and is itself replaced would hand its successor a history cut short at its own first night. When the next platform (or version) replaces the current one, remove the current one's own entry before adding the new one; a later replay of the current one's first nights then judges them cold. A detector config whose entry is removed that way moves to :data:RETIRED_DETECTORS with the config that replaced it, so it stays replaced — not expected to run once that successor, or any config that later replaced it, has — without its history being continued.

predecessor_of

predecessor_of(platform: str) -> str | None

The platform platform replaced, or None.

Source code in k4bench/regression/lineage.py
def predecessor_of(platform: str) -> str | None:
    """The platform *platform* replaced, or ``None``."""
    predecessor = PLATFORM_SUCCESSORS.get(platform)
    return None if predecessor == platform else predecessor

successors_of

successors_of(platform: str) -> tuple[str, ...]

The platforms that replaced platform, name-ascending.

Source code in k4bench/regression/lineage.py
def successors_of(platform: str) -> tuple[str, ...]:
    """The platforms that replaced *platform*, name-ascending."""
    return tuple(sorted(
        successor for successor, predecessor in PLATFORM_SUCCESSORS.items()
        if predecessor == platform and successor != platform
    ))

is_replaced

is_replaced(platform: str) -> bool

Whether some platform has replaced platform.

Source code in k4bench/regression/lineage.py
def is_replaced(platform: str) -> bool:
    """Whether some platform has replaced *platform*."""
    return bool(successors_of(platform))

detector_predecessor_of

detector_predecessor_of(detector: str) -> str | None

The detector config detector replaced, or None.

Source code in k4bench/regression/lineage.py
def detector_predecessor_of(detector: str) -> str | None:
    """The detector config *detector* replaced, or ``None``."""
    predecessor = DETECTOR_SUCCESSORS.get(detector)
    return None if predecessor == detector else predecessor

detector_successors_of

detector_successors_of(detector: str) -> tuple[str, ...]

The detector configs that replaced detector, name-ascending: the one that replaced it, and whatever replaced that one in turn. A retired config is thus no longer expected once any later version has run, even one whose direct successor never produced a run.

Source code in k4bench/regression/lineage.py
def detector_successors_of(detector: str) -> tuple[str, ...]:
    """The detector configs that replaced *detector*, name-ascending: the one
    that replaced it, and whatever replaced that one in turn. A retired config
    is thus no longer expected once any later version has run, even one whose
    direct successor never produced a run."""
    found: set[str] = set()
    frontier = [detector]
    while frontier:
        for successor in _direct_detector_successors(frontier.pop()):
            if successor != detector and successor not in found:
                found.add(successor)
                frontier.append(successor)
    return tuple(sorted(found))

is_detector_replaced

is_detector_replaced(detector: str) -> bool

Whether some detector config has replaced detector.

Source code in k4bench/regression/lineage.py
def is_detector_replaced(detector: str) -> bool:
    """Whether some detector config has replaced *detector*."""
    return bool(detector_successors_of(detector))

predecessor_series

predecessor_series(detector: str, platform: str) -> tuple[tuple[str, str], ...]

The (detector, platform) series the detector / platform series may continue, nearest first: the detector config it replaced on this platform, then this detector config on the platform this one replaced.

A series continues the first candidate that has runs. Both can exist at once — a version bump made after a platform migration, say — and then only one of them normally ran: the new version never ran on the platform retired before it existed.

Source code in k4bench/regression/lineage.py
def predecessor_series(detector: str, platform: str) -> tuple[tuple[str, str], ...]:
    """The ``(detector, platform)`` series the *detector* / *platform* series
    may continue, nearest first: the detector config it replaced on this
    platform, then this detector config on the platform this one replaced.

    A series continues the first candidate that has runs. Both can exist at
    once — a version bump made after a platform migration, say — and then
    only one of them normally ran: the new version never ran on the platform
    retired before it existed.
    """
    candidates = []
    if (previous := detector_predecessor_of(detector)) is not None:
        candidates.append((previous, platform))
    if (previous := predecessor_of(platform)) is not None:
        candidates.append((detector, previous))
    return tuple(candidates)

successor_series

successor_series(detector: str, platform: str) -> tuple[tuple[str, str], ...]

The (detector, platform) series whose runs mean the detector / platform series is no longer expected: the inverse of :func:predecessor_series, plus later detector configs that replaced a retired one.

Source code in k4bench/regression/lineage.py
def successor_series(detector: str, platform: str) -> tuple[tuple[str, str], ...]:
    """The ``(detector, platform)`` series whose runs mean the *detector* /
    *platform* series is no longer expected: the inverse of
    :func:`predecessor_series`, plus later detector configs that replaced a
    retired one."""
    return (
        *((successor, platform) for successor in detector_successors_of(detector)),
        *((detector, successor) for successor in successors_of(platform)),
    )