mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 11:49:28 +00:00
Maintainer decision 2026-10-03: BLOCK precision is shown pooled across families. Req 6.4 (amended; 7.4 annotated): one t_block on a per-family Platt-normalised score (calibrate.fit_map, training folds, inner cross-fit), Wilson 95% LB >= 0.95 over all families' training flags; a family blocks only with >= F = 10 held-out flags and zero errors, else advisory; t_quiet per family keeps >= 90% of training paired positives. evaluate: normalised, pooled_threshold, quiet_threshold, pooled_block_gate, family_floor, apply_floor. experiments: rule pooled_block_gate (normalise: platt, floor, recall), decide_pooled on the pre-floor pooled held-out LB, floor table, quiet_recall and after-floor totals in analyse_rule; exp-003's analysis is unchanged. plane/experiments/exp-004-pooled-block-gate.yaml: procedure and decision rule registered now; units frozen at run time from the post-harvest memory (units_status pending until then). Tests (tests/test_learn_rules.py): pooled threshold where no family alone reaches the bound, no held-out label moves a threshold or the map, a family with one held-out error cannot block, t_quiet and no blind silencing, the pooled verdict, the exp-004 manifest. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
510 lines
24 KiB
Python
510 lines
24 KiB
Python
"""Evaluation of a compiled program on repositories it never saw (learned-decision-engine design 4.4-4.5, Req 5.2,
|
|
7.3, 7.4).
|
|
|
|
For each evaluation fold the program decides every candidate of the fold's groups with memory and demonstrations
|
|
filtered by the fold's boundary. The raw scores are then **cross-fitted** (:mod:`.calibrate`), and the operating
|
|
points are chosen on the *other* folds' calibrated predictions (nested), never on the fold reported:
|
|
|
|
- **BLOCK**: the lowest threshold whose precision has a Wilson 95% lower bound >= 0.95 (delta units when the data
|
|
has them), counting only candidates whose majority verdict is not ``unsure``; none -> ``precision_unreachable``;
|
|
- **ADVISORY**: the highest threshold whose recall is >= 0.90; its precision is reported, not assumed;
|
|
- **DROP** below ADVISORY.
|
|
|
|
Metrics carry repository-cluster bootstrap 95% intervals. A family or language below the pooled floor of positive
|
|
and negative repository groups is reported as ``insufficient data``, never as a number. More than 5% parse failures
|
|
make the run an instrument failure, not a result.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import math
|
|
import random
|
|
import re
|
|
from collections.abc import Callable, Iterable, Mapping, Sequence
|
|
from dataclasses import dataclass, replace
|
|
from typing import Any
|
|
|
|
from .calibrate import Row, check, cross_fit, fit_map, offered
|
|
from .examples import Example
|
|
from .folds import Fold
|
|
from .labels import PER_FAMILY_FLOOR, POOLED_FLOOR
|
|
from .program import Caller, Candidate, Program
|
|
from .retrieve import Target
|
|
from .schema import BY_NAME, SCHEMA_VERSION, feature_set_digest
|
|
|
|
TARGET_PRECISION = 0.95
|
|
TARGET_RECALL = 0.90
|
|
PARSE_FAILURE_LIMIT = 0.05
|
|
BOOTSTRAP = 2000
|
|
_SIGNAL = re.compile(r"\b[a-z]+(?:\.[a-z_]+)+\b")
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Prediction:
|
|
fold: str
|
|
family: str
|
|
group: str
|
|
label: int
|
|
s: float
|
|
verdict: str
|
|
unit: str = "pin"
|
|
language: str = "other"
|
|
source: str = ""
|
|
parse_failed: int = 0
|
|
p: float | None = None
|
|
point: str | None = None # BLOCK | ADVISORY | DROP, from points chosen on the other folds
|
|
pair: str = "" # the two sides of one candidate share it; "" when only one side is present
|
|
|
|
@property
|
|
def blockable(self) -> bool:
|
|
return self.verdict != "unsure"
|
|
|
|
|
|
def wilson_lower(successes: int, n: int, z: float = 1.959964) -> float:
|
|
if n == 0:
|
|
return 0.0
|
|
phat = successes / n
|
|
centre = phat + z * z / (2 * n)
|
|
margin = z * math.sqrt(phat * (1 - phat) / n + z * z / (4 * n * n))
|
|
return (centre - margin) / (1 + z * z / n)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Points:
|
|
block: float | None
|
|
block_offered: bool
|
|
block_reason: str | None
|
|
advisory: float | None
|
|
|
|
def as_dict(self) -> dict[str, Any]:
|
|
return {
|
|
"block": {
|
|
"threshold": self.block,
|
|
"offered": self.block_offered,
|
|
"reason": self.block_reason,
|
|
"target_precision": TARGET_PRECISION,
|
|
},
|
|
"advisory": {"threshold": self.advisory, "target_recall": TARGET_RECALL},
|
|
}
|
|
|
|
|
|
def choose_points(preds: Sequence[Prediction], *, precision: float = TARGET_PRECISION, recall: float = TARGET_RECALL) -> Points:
|
|
"""Operating points on calibrated predictions (``p`` set); rows without ``p`` are not decided."""
|
|
rows = [r for r in preds if r.p is not None]
|
|
thresholds = sorted({float(r.p) for r in rows if r.p is not None})
|
|
deltas = [r for r in rows if r.unit == "delta"]
|
|
blockable = [r for r in (deltas or rows) if r.blockable]
|
|
block = None
|
|
for t in thresholds:
|
|
chosen = [r for r in blockable if (r.p or 0.0) >= t]
|
|
if chosen and wilson_lower(sum(r.label for r in chosen), len(chosen)) >= precision:
|
|
block = t
|
|
break
|
|
positives = sum(r.label for r in rows)
|
|
advisory = None
|
|
for t in reversed(thresholds):
|
|
if positives and sum(r.label for r in rows if (r.p or 0.0) >= t) / positives >= recall:
|
|
advisory = t
|
|
break
|
|
return Points(block, block is not None, None if block is not None else "precision_unreachable", advisory)
|
|
|
|
|
|
def nested_points(preds: Sequence[Prediction]) -> tuple[list[Prediction], dict[str, Points]]:
|
|
"""Each fold's rows decided with the points chosen on the other folds' calibrated predictions."""
|
|
points = {fold: choose_points([r for r in preds if r.fold != fold]) for fold in sorted({r.fold for r in preds})}
|
|
out = []
|
|
for r in preds:
|
|
pts = points[r.fold]
|
|
if r.p is None:
|
|
out.append(r)
|
|
continue
|
|
if pts.block is not None and r.blockable and r.p >= pts.block:
|
|
point = "BLOCK"
|
|
elif pts.advisory is not None and r.p >= pts.advisory:
|
|
point = "ADVISORY"
|
|
else:
|
|
point = "DROP"
|
|
out.append(replace(r, point=point))
|
|
return out, points
|
|
|
|
|
|
BOUNDS = ("wilson", "point")
|
|
|
|
|
|
def precision_threshold(
|
|
rows: Sequence[Prediction], *, precision: float = TARGET_PRECISION, bound: str = "wilson", min_flagged: int = 1
|
|
) -> float | None:
|
|
"""The lowest raw-score threshold ``t`` at which the blockable rows with ``s >= t`` reach ``precision``: their
|
|
Wilson 95% lower bound (``bound="wilson"``) or their point precision over at least ``min_flagged`` rows
|
|
(``bound="point"``). Candidate thresholds are the rows' own scores; ``None`` when no threshold qualifies."""
|
|
if bound not in BOUNDS:
|
|
raise ValueError(f"bound {bound!r} (one of {', '.join(BOUNDS)})")
|
|
blockable = [r for r in rows if r.blockable]
|
|
for t in sorted({r.s for r in blockable}):
|
|
chosen = [r for r in blockable if r.s >= t]
|
|
hits = sum(r.label for r in chosen)
|
|
if len(chosen) < max(1, min_flagged):
|
|
continue
|
|
level = wilson_lower(hits, len(chosen)) if bound == "wilson" else hits / len(chosen)
|
|
if level >= precision:
|
|
return t
|
|
return None
|
|
|
|
|
|
def nested_precision_block(
|
|
preds: Sequence[Prediction],
|
|
*,
|
|
eligible: Callable[[Prediction], bool] | None = None,
|
|
precision: float = TARGET_PRECISION,
|
|
bound: str = "wilson",
|
|
min_flagged: int = 1,
|
|
) -> tuple[list[Prediction], dict[str, dict[str, float | None]]]:
|
|
"""Selective blocking on the raw score, per family: the threshold for outer fold j is chosen on the *other* folds'
|
|
rows of the family only (and, with ``eligible``, only those rows); the held-out fold's labels never enter it.
|
|
Every row of fold j is then decided with that threshold: ``BLOCK`` when blockable and ``s >= t``, else ``DROP``.
|
|
A family whose training folds reach no threshold offers no BLOCK on that fold. Returns the decided rows and the
|
|
thresholds by family and fold."""
|
|
thresholds: dict[str, dict[str, float | None]] = {}
|
|
for family in sorted({r.family for r in preds}):
|
|
rows = [r for r in preds if r.family == family]
|
|
thresholds[family] = {
|
|
fold: precision_threshold(
|
|
[r for r in rows if r.fold != fold and (eligible is None or eligible(r))],
|
|
precision=precision, bound=bound, min_flagged=min_flagged,
|
|
)
|
|
for fold in sorted({r.fold for r in rows})
|
|
} # fmt: skip
|
|
out = []
|
|
for r in preds:
|
|
t = thresholds[r.family][r.fold]
|
|
out.append(replace(r, point="BLOCK" if t is not None and r.blockable and r.s >= t else "DROP"))
|
|
return out, thresholds
|
|
|
|
|
|
def calibrated_block(preds: Sequence[Prediction], *, seed: int = 0, **floors: int) -> tuple[list[Prediction], dict[str, Any]]:
|
|
"""Today's BLOCK rule, per family as the slice-2 evaluation applied it: the Platt map cross-fitted on the other
|
|
folds, the calibration check on the family's held-out calibrated predictions, the BLOCK point chosen on the other
|
|
folds' calibrated predictions (nested); a row is ``BLOCK`` only when the family's calibration holds. Returns the
|
|
decided rows and, per family, the check and the calibrated thresholds by fold."""
|
|
out: list[Prediction] = []
|
|
info: dict[str, Any] = {}
|
|
for family in sorted({r.family for r in preds}):
|
|
rows = [r for r in preds if r.family == family]
|
|
ps, _ = cross_fit([Row(r.fold, r.family, r.group, r.label, r.s) for r in rows], **floors)
|
|
calibrated = [replace(r, p=p) for r, p in zip(rows, ps, strict=True)]
|
|
decided = [r for r in calibrated if r.p is not None]
|
|
checked = (
|
|
check([float(r.p) for r in decided if r.p is not None], [r.label for r in decided], [r.group for r in decided], seed=seed)
|
|
if len({r.label for r in decided}) == 2
|
|
else None
|
|
)
|
|
holds = bool(checked and checked.holds)
|
|
nested, points = nested_points(calibrated)
|
|
out += [replace(r, point="BLOCK" if holds and r.point == "BLOCK" else "DROP") for r in nested]
|
|
info[family] = {
|
|
"calibration_holds": holds, "ece": None if checked is None else round(checked.ece, 4),
|
|
"thresholds_by_fold": {f: p.block for f, p in points.items()},
|
|
"reason_by_fold": {f: ("calibration" if not holds else p.block_reason) for f, p in points.items()},
|
|
} # fmt: skip
|
|
return out, info
|
|
|
|
|
|
# --- the pooled BLOCK gate (Req 6.4, amended 2026-10-03; exp-004) ---------------------------------------------------------
|
|
|
|
FAMILY_FLOOR = 10 # a family blocks only with at least this many held-out flags and none of them wrong
|
|
|
|
|
|
def normalised(train: Sequence[Prediction], rows: Sequence[Prediction], **floors: int) -> list[float | None]:
|
|
"""``rows``' raw scores on one scale across families: the per-family Platt map of :func:`.calibrate.fit_map`
|
|
(pooled slope with a per-family intercept between the floors) fitted on ``train`` only. ``None`` for a family the
|
|
training rows cannot map (below the pooled floor): it cannot block on that fold."""
|
|
cmap = fit_map([Row(r.fold, r.family, r.group, r.label, r.s) for r in train], **floors)
|
|
return [cmap.apply(r.family, r.s) for r in rows]
|
|
|
|
|
|
def pooled_threshold(rows: Sequence[Prediction], *, precision: float = TARGET_PRECISION) -> float | None:
|
|
"""The lowest normalised score ``t`` at which the blockable rows of every family together with ``p >= t`` have a
|
|
Wilson 95% lower bound of precision >= ``precision``; candidate thresholds are the rows' own ``p``."""
|
|
blockable = [r for r in rows if r.blockable and r.p is not None]
|
|
for t in sorted({float(r.p) for r in blockable if r.p is not None}):
|
|
chosen = [r for r in blockable if (r.p or 0.0) >= t]
|
|
if wilson_lower(sum(r.label for r in chosen), len(chosen)) >= precision:
|
|
return t
|
|
return None
|
|
|
|
|
|
def quiet_threshold(rows: Sequence[Prediction], *, recall: float = TARGET_RECALL) -> float | None:
|
|
"""The highest normalised score ``t`` that keeps at least ``recall`` of the rows' positives at or above it."""
|
|
positives = sorted((float(r.p) for r in rows if r.label == 1 and r.p is not None), reverse=True)
|
|
if not positives:
|
|
return None
|
|
return positives[math.ceil(recall * len(positives) - 1e-9) - 1]
|
|
|
|
|
|
def pooled_block_gate(
|
|
preds: Sequence[Prediction],
|
|
*,
|
|
eligible: Callable[[Prediction], bool] | None = None,
|
|
precision: float = TARGET_PRECISION,
|
|
recall: float = TARGET_RECALL,
|
|
**floors: int,
|
|
) -> tuple[list[Prediction], dict[str, dict[str, Any]]]:
|
|
"""One BLOCK threshold for every family, on a normalised score, chosen per outer fold j on the other folds only.
|
|
|
|
The training rows (``eligible`` rows of the other folds) are normalised by an inner cross-fit: each training fold
|
|
k by a map fitted on the training folds other than k, so ``t_block`` is chosen on out-of-sample scores. ``t_block``
|
|
is the lowest normalised score whose pooled Wilson 95% lower bound of precision is >= ``precision``; ``t_quiet``
|
|
per family is the highest one keeping >= ``recall`` of that family's training positives. Fold j's rows are then
|
|
normalised by the map fitted on all its training rows and decided: ``BLOCK`` when blockable and ``p >= t_block``;
|
|
``ADVISORY`` when ``p >= t_quiet`` (or the family has no map, or no ``t_quiet``: nothing is silenced blind);
|
|
``DROP`` (silent) below ``t_quiet``. Fold j's labels never enter its thresholds or its map."""
|
|
chosen = [r for r in preds if eligible is None or eligible(r)]
|
|
families = sorted({r.family for r in preds})
|
|
decided: dict[int, Prediction] = {}
|
|
gates: dict[str, dict[str, Any]] = {}
|
|
for fold in sorted({r.fold for r in preds}):
|
|
train = [r for r in chosen if r.fold != fold]
|
|
inner: list[Prediction] = []
|
|
for k in sorted({r.fold for r in train}):
|
|
mine = [r for r in train if r.fold == k]
|
|
inner += [replace(r, p=p) for r, p in zip(mine, normalised([r for r in train if r.fold != k], mine, **floors), strict=True)
|
|
if p is not None] # fmt: skip
|
|
t_block = pooled_threshold(inner, precision=precision)
|
|
t_quiet = {f: quiet_threshold([r for r in inner if r.family == f], recall=recall) for f in families}
|
|
held = [(i, r) for i, r in enumerate(preds) if r.fold == fold]
|
|
for (i, r), p in zip(held, normalised(train, [r for _, r in held], **floors), strict=True):
|
|
quiet = t_quiet[r.family]
|
|
if p is not None and t_block is not None and r.blockable and p >= t_block:
|
|
point = "BLOCK"
|
|
elif p is None or quiet is None or p >= quiet:
|
|
point = "ADVISORY"
|
|
else:
|
|
point = "DROP"
|
|
decided[i] = replace(r, p=p, point=point)
|
|
gates[fold] = {"t_block": t_block, "t_quiet": t_quiet, "training_rows": len(train), "normalised_training_rows": len(inner)}
|
|
return [decided[i] for i in range(len(preds))], gates
|
|
|
|
|
|
def family_floor(rows: Sequence[Prediction], *, floor: int = FAMILY_FLOOR) -> dict[str, dict[str, Any]]:
|
|
"""Per family, its held-out ``BLOCK`` flags among ``rows`` (the primary population, every fold): it may block only
|
|
when none is wrong and there are at least ``floor``; otherwise its flags are reported as ADVISORY."""
|
|
out: dict[str, dict[str, Any]] = {}
|
|
for family in sorted({r.family for r in rows}):
|
|
flagged = [r for r in rows if r.family == family and r.point == "BLOCK"]
|
|
errors = sum(1 for r in flagged if r.label == 0)
|
|
out[family] = {"flagged": len(flagged), "errors": errors, "floor": floor, "may_block": errors == 0 and len(flagged) >= floor}
|
|
return out
|
|
|
|
|
|
def apply_floor(rows: Sequence[Prediction], floors: Mapping[str, Mapping[str, Any]]) -> list[Prediction]:
|
|
"""``BLOCK`` becomes ``ADVISORY`` in every family that does not pass :func:`family_floor`."""
|
|
return [replace(r, point="ADVISORY") if r.point == "BLOCK" and not floors.get(r.family, {}).get("may_block") else r for r in rows]
|
|
|
|
|
|
def _at(rows: Sequence[Prediction], level: str) -> dict[str, float | None]:
|
|
flagged = [r for r in rows if r.point in (("BLOCK",) if level == "block" else ("BLOCK", "ADVISORY"))]
|
|
positives = sum(r.label for r in rows)
|
|
benign = [r for r in rows if r.label == 0 and r.unit == "delta"]
|
|
return {
|
|
"recall": sum(r.label for r in flagged) / positives if positives else None,
|
|
"precision": sum(r.label for r in flagged) / len(flagged) if flagged else None,
|
|
"false_per_100_benign_deltas": 100 * sum(1 for r in benign if r.point == "BLOCK") / len(benign)
|
|
if benign and level == "block"
|
|
else None,
|
|
}
|
|
|
|
|
|
def cluster_bootstrap(
|
|
rows: Sequence[Prediction], stat: Callable[[Sequence[Prediction]], float | None], *, resamples: int = BOOTSTRAP, seed: int = 0
|
|
) -> tuple[float, float] | None:
|
|
by_group: dict[str, list[Prediction]] = {}
|
|
for r in rows:
|
|
by_group.setdefault(r.group, []).append(r)
|
|
names = sorted(by_group)
|
|
if not names:
|
|
return None
|
|
rng = random.Random(seed)
|
|
values = []
|
|
for _ in range(resamples):
|
|
sample = [r for g in (rng.choice(names) for _ in names) for r in by_group[g]]
|
|
value = stat(sample)
|
|
if value is not None:
|
|
values.append(value)
|
|
if not values:
|
|
return None
|
|
values.sort()
|
|
return values[int(0.025 * (len(values) - 1))], values[int(0.975 * (len(values) - 1))]
|
|
|
|
|
|
def summary(rows: Sequence[Prediction], *, resamples: int = BOOTSTRAP, seed: int = 0) -> dict[str, Any]:
|
|
out: dict[str, Any] = {"candidates": len(rows), "groups": len({r.group for r in rows})}
|
|
|
|
def statistic(level: str, name: str) -> Callable[[Sequence[Prediction]], float | None]:
|
|
return lambda sample: _at(sample, level)[name]
|
|
|
|
for level in ("block", "advisory"):
|
|
point = _at(rows, level)
|
|
out[level] = {
|
|
name: {"value": value, "ci": cluster_bootstrap(rows, statistic(level, name), resamples=resamples, seed=seed)}
|
|
for name, value in point.items()
|
|
if value is not None
|
|
}
|
|
return out
|
|
|
|
|
|
def floor_status(rows: Sequence[Prediction]) -> str:
|
|
pos = len({r.group for r in rows if r.label == 1})
|
|
neg = len({r.group for r in rows if r.label == 0})
|
|
level = min(pos, neg)
|
|
return "per_family" if level >= PER_FAMILY_FLOOR else "pooled" if level >= POOLED_FLOOR else "insufficient data"
|
|
|
|
|
|
def strata(rows: Sequence[Prediction], key: Callable[[Prediction], str], **kw: Any) -> dict[str, Any]:
|
|
out: dict[str, Any] = {}
|
|
for name in sorted({key(r) for r in rows}):
|
|
part = [r for r in rows if key(r) == name]
|
|
status = floor_status(part)
|
|
out[name] = {"status": status} if status == "insufficient data" else {"status": status, **summary(part, **kw)}
|
|
return out
|
|
|
|
|
|
def explain(rationale: str, x: Mapping[str, Any]) -> list[str]:
|
|
"""The signals the rationale names that the record holds (a name it invents is dropped)."""
|
|
named = dict.fromkeys(m.group(0) for m in _SIGNAL.finditer(rationale))
|
|
return [n for n in named if n in BY_NAME and n in x and x[n] is not None]
|
|
|
|
|
|
def incompatible(artifact: Mapping[str, Any], record: Mapping[str, Any], signature_digest: str) -> str | None:
|
|
"""Why a program may not decide a record (the caller falls back to today's behaviour and says so)."""
|
|
if artifact.get("schema_version") != SCHEMA_VERSION or record.get("schema_version") != SCHEMA_VERSION:
|
|
return "schema version differs"
|
|
if artifact.get("feature_set_digest") != feature_set_digest() or record.get("feature_set_digest") != feature_set_digest():
|
|
return "feature set differs"
|
|
if (artifact.get("signature") or {}).get("digest") != signature_digest:
|
|
return "signature differs"
|
|
if artifact.get("profile") != record.get("profile"):
|
|
return "profile differs"
|
|
return None
|
|
|
|
|
|
def targets(folds: Sequence[Fold], examples: Sequence[Example], *, skip: Iterable[str] = ()) -> list[tuple[Example, Fold]]:
|
|
"""(example, fold) for every example of every usable fold's evaluated groups; ``skip`` families are never asked
|
|
(the families below the floor: no money is spent on them)."""
|
|
skipped = set(skip)
|
|
return [(e, f) for f in folds if f.status == "ok" for e in examples if e.group in f.eval_groups and e.family not in skipped]
|
|
|
|
|
|
def predict(
|
|
program: Program,
|
|
folds: Sequence[Fold],
|
|
caller: Caller,
|
|
candidate_of: Callable[[Any], Candidate | None],
|
|
*,
|
|
seed: int = 0,
|
|
pairs: Sequence[tuple[Example, Fold]] | None = None,
|
|
) -> list[Prediction]:
|
|
"""Decide every example of every fold's evaluated groups (or the given ``pairs``), each under its own fold."""
|
|
out: list[Prediction] = []
|
|
for example, fold in pairs if pairs is not None else targets(folds, program.memory):
|
|
candidate = candidate_of(example)
|
|
if candidate is None:
|
|
continue
|
|
decision = program.decide(candidate, fold, caller, seed=seed)
|
|
out.append(
|
|
Prediction(
|
|
fold.name, example.family, example.group, example.label, decision.s, decision.verdict, example.unit, example.language,
|
|
example.source, decision.parse_failed,
|
|
)
|
|
) # fmt: skip
|
|
return out
|
|
|
|
|
|
def unevaluable_families(examples: Sequence[Example], *, floor: int = POOLED_FLOOR) -> list[str]:
|
|
"""Families below the pooled floor of positive and negative repository groups: reported, never evaluated."""
|
|
out = []
|
|
for family in sorted({e.family for e in examples}):
|
|
pos = len({e.group for e in examples if e.family == family and e.label == 1})
|
|
neg = len({e.group for e in examples if e.family == family and e.label == 0})
|
|
if min(pos, neg) < floor:
|
|
out.append(family)
|
|
return out
|
|
|
|
|
|
def learning_curve(
|
|
program: Program,
|
|
pairs: Sequence[tuple[Example, Fold]],
|
|
caller: Caller,
|
|
candidate_of: Callable[[Any], Candidate | None],
|
|
*,
|
|
fractions: Sequence[float] = (0.0, 0.25, 0.5, 1.0),
|
|
seed: int = 0,
|
|
) -> list[dict[str, Any]]:
|
|
"""The metric as a function of memory size: a seeded share of the memory's groups, the same fixed candidates."""
|
|
from .compile import balanced_brier
|
|
|
|
groups = sorted({e.group for e in program.memory}, key=lambda g: hashlib.sha256(f"{seed}\x00curve\x00{g}".encode()).hexdigest())
|
|
points = []
|
|
for fraction in fractions:
|
|
kept = set(groups[: int(len(groups) * fraction + 0.5)])
|
|
sub = replace(program, memory=[e for e in program.memory if e.group in kept])
|
|
preds = predict(sub, [], caller, candidate_of, seed=seed, pairs=pairs)
|
|
brier = balanced_brier([(p.family, p.label, p.group, p.s) for p in preds])
|
|
points.append({"memory_fraction": fraction, "memory_groups": len(kept), "candidates": len(preds), "balanced_brier": brier})
|
|
return points
|
|
|
|
|
|
def evaluate(preds: Sequence[Prediction], *, resamples: int = BOOTSTRAP, seed: int = 0, **floors: int) -> dict[str, Any]:
|
|
"""Cross-fitted calibration, its check, nested operating points and the report (counts and intervals only)."""
|
|
failed = sum(r.parse_failed for r in preds)
|
|
answers = len(preds) or 1
|
|
rows = [Row(r.fold, r.family, r.group, r.label, r.s) for r in preds]
|
|
ps, maps = cross_fit(rows, **floors)
|
|
calibrated = [replace(r, p=p) for r, p in zip(preds, ps, strict=True)]
|
|
decided = [r for r in calibrated if r.p is not None]
|
|
checked = (
|
|
check([float(r.p) for r in decided if r.p is not None], [r.label for r in decided], [r.group for r in decided], seed=seed)
|
|
if decided
|
|
else None
|
|
)
|
|
nested, points = nested_points(calibrated)
|
|
report: dict[str, Any] = {
|
|
"status": "instrument failure" if failed / answers > PARSE_FAILURE_LIMIT else "ok",
|
|
"parse_failed_share": round(failed / answers, 4),
|
|
"calibration": checked.as_dict() if checked else None,
|
|
"offered": offered(checked) if checked else {"block": {"offered": False, "reason": "insufficient data"}},
|
|
"points_by_fold": {f: p.as_dict() for f, p in points.items()},
|
|
"maps_by_fold": {f: m.as_dict() for f, m in maps.items()},
|
|
"overall": summary([r for r in nested if r.p is not None], resamples=resamples, seed=seed),
|
|
"by_family": strata(nested, lambda r: r.family, resamples=resamples, seed=seed),
|
|
"by_language": strata(nested, lambda r: r.language, resamples=resamples, seed=seed),
|
|
"undecided": sum(1 for r in nested if r.p is None),
|
|
}
|
|
if checked is not None and not checked.holds:
|
|
report["overall"]["block"] = {"offered": False, "reason": "calibration"}
|
|
return report
|
|
|
|
|
|
def candidate_from_example(
|
|
excerpt_text: Callable[[str], str | None], vectors: Mapping[str, Sequence[float]] | None = None
|
|
) -> Callable[[Any], Candidate | None]:
|
|
"""An evaluation candidate from a memory example: its record and stored excerpt (a 40-line example excerpt)."""
|
|
|
|
def make(example: Any) -> Candidate | None:
|
|
code = excerpt_text(example.excerpt_sha)
|
|
if not code:
|
|
return None
|
|
return Candidate(Target.of(example, (vectors or {}).get(example.excerpt_sha)), code, example.language, tuple(example.roles))
|
|
|
|
return make
|
|
|
|
|
|
__all__ = [
|
|
"BOUNDS", "FAMILY_FLOOR", "PARSE_FAILURE_LIMIT", "TARGET_PRECISION", "TARGET_RECALL", "Points", "Prediction", "apply_floor",
|
|
"calibrated_block", "family_floor", "normalised", "pooled_block_gate", "pooled_threshold", "quiet_threshold",
|
|
"candidate_from_example", "choose_points", "cluster_bootstrap", "evaluate", "explain", "floor_status", "incompatible",
|
|
"learning_curve", "nested_points", "nested_precision_block", "precision_threshold", "predict", "strata", "summary", "targets",
|
|
"unevaluable_families",
|
|
"wilson_lower",
|
|
] # fmt: skip
|