99 lines
2.8 KiB
Python
99 lines
2.8 KiB
Python
from __future__ import annotations
|
|
|
|
from collections import Counter
|
|
|
|
import pytest
|
|
|
|
from k1link.compute.e37_acceptance_contract import (
|
|
E37AcceptanceContractError,
|
|
assign_split,
|
|
derive_reference_labels,
|
|
)
|
|
|
|
|
|
def _decision(
|
|
*,
|
|
detector: str,
|
|
ownership: str,
|
|
cause: str | None = None,
|
|
effective_stratum: str | None = "agree",
|
|
) -> dict[str, object]:
|
|
return {
|
|
"detector_assessment": detector,
|
|
"point_ownership": ownership,
|
|
"cause_code": cause,
|
|
"effective_stratum": effective_stratum,
|
|
}
|
|
|
|
|
|
def test_r0_reference_projection_keeps_dimensions_independent() -> None:
|
|
assert derive_reference_labels(
|
|
_decision(detector="valid", ownership="object"),
|
|
None,
|
|
) == ("object-present", "object-associated", "current")
|
|
assert derive_reference_labels(
|
|
_decision(
|
|
detector="false-positive",
|
|
ownership="surface-or-background",
|
|
cause="time_mismatch",
|
|
),
|
|
None,
|
|
) == ("background-or-noise", "rejected-nonobject", "stale")
|
|
assert derive_reference_labels(
|
|
_decision(
|
|
detector="not-applicable",
|
|
ownership="insufficient-support",
|
|
effective_stratum="camera-only",
|
|
),
|
|
None,
|
|
) == ("unknown", "insufficient-support", "unavailable")
|
|
|
|
|
|
def test_human_exception_only_overrides_the_ambiguous_reference() -> None:
|
|
ambiguous = _decision(
|
|
detector="insufficient-evidence",
|
|
ownership="insufficient-evidence",
|
|
effective_stratum=None,
|
|
)
|
|
assert derive_reference_labels(ambiguous, "object-present") == (
|
|
"occupied-environment",
|
|
"independent-occupied",
|
|
"unavailable",
|
|
)
|
|
assert derive_reference_labels(ambiguous, "background-or-noise") == (
|
|
"background-or-noise",
|
|
"rejected-nonobject",
|
|
"unavailable",
|
|
)
|
|
|
|
|
|
def test_split_is_deterministic_balanced_and_complete() -> None:
|
|
rows = [
|
|
{
|
|
"item_id": f"item-{index:03d}",
|
|
"source_stratum": "agree" if index % 2 else "conflict",
|
|
"range_bucket": "near" if index % 3 else "far",
|
|
}
|
|
for index in range(60)
|
|
]
|
|
first = assign_split(
|
|
rows,
|
|
seed="frozen-seed",
|
|
validation_fraction=0.3,
|
|
)
|
|
second = assign_split(
|
|
list(reversed(rows)),
|
|
seed="frozen-seed",
|
|
validation_fraction=0.3,
|
|
)
|
|
assert first == second
|
|
assert set(first) == {row["item_id"] for row in rows}
|
|
assert Counter(first.values())["validation"] == 18
|
|
|
|
|
|
def test_split_rejects_mutable_or_invalid_fraction() -> None:
|
|
with pytest.raises(E37AcceptanceContractError):
|
|
assign_split([], seed="", validation_fraction=0.3)
|
|
with pytest.raises(E37AcceptanceContractError):
|
|
assign_split([], seed="seed", validation_fraction=0.9)
|