Preserve source, binary, and runtime evidence boundaries while making precision and performance regressions reproducible. - decompose analysis orchestration into bounded modules - add differential, metamorphic, organic, and Yams change-time gates - fix detector, P-Code, fuzz sanitizer, and report identity precision
46 lines
1.3 KiB
Python
46 lines
1.3 KiB
Python
"""Repeated-run finding stability metrics."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Iterable, Sequence
|
|
from typing import Any
|
|
|
|
from .metrics import distribution_summary
|
|
|
|
|
|
def jaccard(left: set[str], right: set[str]) -> float:
|
|
union = left | right
|
|
return len(left & right) / len(union) if union else 1.0
|
|
|
|
|
|
def stability_summary(runs: Sequence[Iterable[str]]) -> dict[str, Any]:
|
|
sets = [set(run) for run in runs]
|
|
if not sets:
|
|
return {
|
|
"run_count": 0,
|
|
"exact": False,
|
|
"intersection_count": 0,
|
|
"union_count": 0,
|
|
"churn_count": 0,
|
|
"per_run_counts": [],
|
|
"pairwise_jaccard": distribution_summary([]),
|
|
}
|
|
|
|
intersection = set.intersection(*sets)
|
|
union = set.union(*sets)
|
|
pairwise = [
|
|
jaccard(sets[left], sets[right])
|
|
for left in range(len(sets))
|
|
for right in range(left + 1, len(sets))
|
|
]
|
|
if len(sets) == 1:
|
|
pairwise = [1.0]
|
|
return {
|
|
"run_count": len(sets),
|
|
"exact": all(current == sets[0] for current in sets[1:]),
|
|
"intersection_count": len(intersection),
|
|
"union_count": len(union),
|
|
"churn_count": len(union - intersection),
|
|
"per_run_counts": [len(current) for current in sets],
|
|
"pairwise_jaccard": distribution_summary(pairwise),
|
|
}
|