feat: modernize PSO and add convergence research

Migrate the package and examples to the tensor-native PyTorch implementation, add benchmark evidence, and add the guarded post-training convergence protocol with TensorBoard progress monitoring and hash-verified recovery.

Constraint: Preserve one-shot official-test sealing and auditable research artifacts

Rejected: Commit local .omc runs and downloaded datasets | multi-gigabyte runtime state is machine-local

Confidence: high

Scope-risk: broad

Not-tested: Production CUDA run on pieroot-server
This commit is contained in:
2026-09-07 22:03:25 +09:00
parent 9745bb7ad4
commit 813433000a
120 changed files with 198148 additions and 4745 deletions
@@ -0,0 +1,974 @@
"""Independent evaluator for the post-training model-convergence protocol.
This module is deliberately data-only: it reads JSON manifests, result records and
stored prediction records. It never imports an adapter, optional detection
package, dataset, checkpoint, or model. A result is useful only when all of the
frozen matrix and sealing invariants can be demonstrated from the saved evidence.
"""
from __future__ import annotations
import argparse
import hashlib
import json
import math
import os
import random
import sys
import tempfile
from pathlib import Path
from typing import Any, Iterable, Mapping, Sequence
EVALUATOR_VERSION = "POST-TRAINING-MODEL-CONVERGENCE-EVALUATOR 1.0.0"
PROTOCOL_VERSION = "post-training-model-convergence-1.0.0"
WORKLOADS = ("cifar10_resnet18", "cifar10_resnet50", "voc_yolo11n")
CLASSIFICATION_WORKLOADS = WORKLOADS[:2]
DETECTION_WORKLOAD = WORKLOADS[2]
BASE_SEEDS = (501, 502, 503)
SWARM_SEEDS = (601, 602, 603)
SPLIT_SEED = 20260908
PROJECTION_SEED = 20260909
BOOTSTRAP_SEED = 20260910
PARTICLES = 12
PRIMARY_GENERATIONS = 60
ENSEMBLE_GENERATIONS = 20
PRIMARY_QUERIES = PARTICLES * PRIMARY_GENERATIONS
ENSEMBLE_QUERIES = PARTICLES * ENSEMBLE_GENERATIONS
PRIMARY_RUNS = len(WORKLOADS) * len(BASE_SEEDS) * len(SWARM_SEEDS)
ENSEMBLE_RUNS = len(WORKLOADS) * len(SWARM_SEEDS)
TOTAL_PSO_QUERIES = PRIMARY_RUNS * PRIMARY_QUERIES + ENSEMBLE_RUNS * ENSEMBLE_QUERIES
OBJECTIVE_SAMPLES = {"cifar10_resnet18": 1024, "cifar10_resnet50": 1024, "voc_yolo11n": 512}
TOTAL_CANDIDATE_SAMPLES = sum(
(len(BASE_SEEDS) * len(SWARM_SEEDS) * PRIMARY_QUERIES + len(SWARM_SEEDS) * ENSEMBLE_QUERIES)
* OBJECTIVE_SAMPLES[w] for w in WORKLOADS
)
BOOTSTRAP_RESAMPLES = 2000
BOOTSTRAP_ALPHA = 0.05 / 6.0
ISSUE_CATEGORIES = (
"schema", "provenance", "matrix", "accounting", "seal", "selection",
"finite", "metrics", "plateau", "overfit", "leakage", "bootstrap", "gates",
)
def _issue(issues: dict[str, list[str]], category: str, message: str) -> None:
issues.setdefault(category, []).append(message)
def _finite(value: Any) -> bool:
return isinstance(value, (int, float)) and not isinstance(value, bool) and math.isfinite(float(value))
def _walk_nonfinite(value: Any, path: str = "") -> list[str]:
out: list[str] = []
if isinstance(value, float) and not math.isfinite(value):
out.append(path or "$")
elif isinstance(value, Mapping):
for key, item in value.items():
out.extend(_walk_nonfinite(item, f"{path}.{key}" if path else str(key)))
elif isinstance(value, (list, tuple)):
for i, item in enumerate(value):
out.extend(_walk_nonfinite(item, f"{path}[{i}]"))
return out
def _json(path: Path) -> Any:
with path.open("r", encoding="utf-8") as handle:
return json.load(handle)
def _canonical(value: Any) -> bytes:
return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
def _sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def _atomic_json(path: Path, value: Any) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
fd, name = tempfile.mkstemp(prefix=f".{path.name}.", dir=str(path.parent))
try:
with os.fdopen(fd, "w", encoding="utf-8") as handle:
json.dump(value, handle, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
handle.write("\n")
handle.flush()
os.fsync(handle.fileno())
os.replace(name, path)
except BaseException:
try:
os.unlink(name)
except OSError:
pass
raise
def _number(value: Any, *keys: str) -> float | None:
if isinstance(value, Mapping):
for key in keys:
candidate = value.get(key)
if _finite(candidate):
return float(candidate)
for candidate in value.values():
found = _number(candidate, *keys)
if found is not None:
return found
elif isinstance(value, (list, tuple)):
for candidate in value:
found = _number(candidate, *keys)
if found is not None:
return found
return None
def _int(value: Any, *keys: str) -> int | None:
number = _number(value, *keys)
if number is None or not number.is_integer():
return None
return int(number)
def _same(a: Any, b: Any, tol: float = 1e-9) -> bool:
return _finite(a) and _finite(b) and math.isclose(float(a), float(b), rel_tol=tol, abs_tol=tol)
def _resolve_json(root: Path, value: Any) -> Any:
"""Resolve a saved JSON prediction reference without opening model/data files."""
if isinstance(value, Mapping):
for key in ("path", "file", "artifact", "prediction_artifact", "predictions_path"):
ref = value.get(key)
if isinstance(ref, str) and ref.lower().endswith((".json", ".jsonl", ".pt")):
return _resolve_json(root, ref)
return value
if isinstance(value, str) and value.lower().endswith((".json", ".jsonl", ".pt")):
path = (root / value).resolve()
if root.resolve() not in path.parents:
raise ValueError(f"prediction artifact escapes run root: {value}")
if not path.is_file():
raise FileNotFoundError(path)
if path.suffix == ".jsonl":
return [_json_line for _json_line in (json.loads(line) for line in path.read_text(encoding="utf-8").splitlines()) if _json_line]
if path.suffix == ".pt":
try:
import torch
return torch.load(path, map_location="cpu", weights_only=True)
except (ImportError, OSError, RuntimeError, TypeError, ValueError) as exc:
raise ValueError(f"unable to load weights-only prediction artifact: {path}: {exc}") from exc
return _json(path)
return value
def _hash_manifest(root: Path, manifest: Mapping[str, Any], issues: dict[str, list[str]]) -> bool:
good = True
if manifest.get("protocol_version") != PROTOCOL_VERSION:
_issue(issues, "provenance", f"frozen manifest protocol mismatch: {manifest.get('protocol_version')!r}")
good = False
if manifest.get("state") != "frozen":
_issue(issues, "seal", f"frozen manifest state must be 'frozen', got {manifest.get('state')!r}")
good = False
declared = manifest.get("manifest_hash")
payload = {key: manifest[key] for key in ("protocol_version", "config", "artifacts", "state") if key in manifest}
if not isinstance(declared, str) or hashlib.sha256(_canonical(payload)).hexdigest() != declared:
_issue(issues, "seal", "frozen_manifest.json self-hash mismatch")
good = False
artifacts = manifest.get("artifacts")
if not isinstance(artifacts, Mapping) or not artifacts:
_issue(issues, "schema", "frozen manifest requires a non-empty artifacts map")
return False
for name, expected in artifacts.items():
if not isinstance(name, str) or not isinstance(expected, str) or len(expected) != 64:
_issue(issues, "schema", f"invalid frozen artifact hash declaration: {name!r}")
good = False
continue
path = (root / name).resolve()
if root.resolve() not in path.parents:
_issue(issues, "seal", f"frozen artifact escapes run root: {name}")
good = False
elif not path.is_file():
_issue(issues, "seal", f"frozen artifact is missing: {name}")
good = False
elif _sha256(path) != expected:
_issue(issues, "seal", f"frozen artifact hash drift: {name}")
good = False
return good
def _config_checks(config: Any, issues: dict[str, list[str]], workload_id: str | None = None) -> None:
if not isinstance(config, Mapping):
_issue(issues, "schema", "missing or non-object config")
return
expected: dict[str, Any] = {
"protocol_version": PROTOCOL_VERSION, "split_seed": SPLIT_SEED,
"projection_seed": PROJECTION_SEED, "bootstrap_seed": BOOTSTRAP_SEED,
"particle_count": PARTICLES, "pso_generations": PRIMARY_GENERATIONS,
"residual_dimension": 64, "residual_bound": 1.0, "initial_radius": 0.25,
"objective_checkpoints": [0, 10, 20, 30, 40, 50, 60],
"base_seeds": list(BASE_SEEDS), "swarm_seeds": list(SWARM_SEEDS),
}
for key, value in expected.items():
got = config.get(key)
if got != value and not (isinstance(got, tuple) and list(got) == value):
_issue(issues, "provenance", f"config.{key} must be {value!r}, got {got!r}")
ids = config.get("workload_ids")
if ids is not None and sorted(ids) != sorted(WORKLOADS):
_issue(issues, "matrix", f"config.workload_ids must be {list(WORKLOADS)!r}")
if workload_id and config.get("workload_id") not in (None, workload_id):
_issue(issues, "matrix", f"config.workload_id disagrees with {workload_id}")
def _counter(value: Any, keys: Sequence[str]) -> int | None:
if not isinstance(value, Mapping):
return None
for key in keys:
candidate = value.get(key)
if isinstance(candidate, int) and not isinstance(candidate, bool):
return candidate
return None
def _check_leakage(result: Mapping[str, Any], issues: dict[str, list[str]], workload: str) -> None:
leakage = result.get("leakage_counters")
if not isinstance(leakage, Mapping):
_issue(issues, "schema", f"{workload}: missing leakage_counters")
leakage = {}
loaded_before = leakage.get("official_test_data_loaded_before_freeze", leakage.get("test_data_loaded_before_freeze"))
if loaded_before is not False:
_issue(issues, "leakage", f"{workload}: official test data must be explicitly marked not loaded before freeze")
evaluated_before = leakage.get("official_test_evaluations_before_freeze", leakage.get("test_evaluations_before_freeze", leakage.get("official_test_forward_passes_before_freeze")))
if evaluated_before != 0:
_issue(issues, "leakage", f"{workload}: official test exposure before freeze must be explicitly zero")
for key in ("official_test_data_loaded_before_freeze", "official_test_evaluations_before_freeze"):
if key in result and ((key.endswith("freeze") and result[key] not in (False, 0))):
_issue(issues, "leakage", f"{workload}: contradictory top-level {key}")
construction = _counter(leakage, ("official_test_construction", "official_test_dataset_construction"))
if construction != 1:
_issue(issues, "leakage", f"{workload}: official test construction must equal one, got {construction}")
forwards = _counter(leakage, ("official_test_forward_passes", "official_test_evaluations"))
if forwards is None or forwards < 1:
_issue(issues, "leakage", f"{workload}: official test forward/evaluation ledger must be positive, got {forwards}")
confirmation = result.get("confirmation")
if not isinstance(confirmation, Mapping):
_issue(issues, "schema", f"{workload}: missing confirmation record")
return
for key in ("second_confirmation", "confirmation_repeated", "post_test_tuning", "post_test_reruns"):
if confirmation.get(key) not in (None, False, 0, []):
_issue(issues, "leakage", f"{workload}: forbidden repeated confirmation/tuning flag {key}")
for key in ("official_test_data_loaded_before_freeze", "official_test_evaluations_before_freeze"):
if key in confirmation and confirmation[key] not in (False, 0):
_issue(issues, "leakage", f"{workload}: confirmation contradicts sealed pre-freeze {key}")
def _record_queries(record: Mapping[str, Any]) -> tuple[int | None, int | None]:
counters = record.get("counters") if isinstance(record.get("counters"), Mapping) else record
queries = _int(counters, "objective_queries", "queries", "query_count", "total_queries", "evaluated_queries")
samples = _int(counters, "objective_samples", "samples", "sample_evaluations", "candidate_sample_evaluations", "total_sample_evaluations")
return queries, samples
def _seed_from_key(key: Any) -> int | None:
try:
text = str(key)
if text.isdigit():
return int(text)
except Exception:
pass
return None
def _collect_cells(node: Any, base: int | None = None, swarm: int | None = None) -> list[dict[str, Any]]:
"""Collect cells from either explicit records or base->swarm mappings."""
found: list[dict[str, Any]] = []
if isinstance(node, Mapping):
b = _int(node, "base_seed") if _int(node, "base_seed") is not None else base
s = _int(node, "swarm_seed") if _int(node, "swarm_seed") is not None else swarm
if s is None:
s = _int(node, "seed")
if b is not None and s is not None and any(k in node for k in ("counters", "objective_queries", "objective_samples", "queries", "samples", "generation", "endpoint", "metrics")):
item = dict(node); item.setdefault("base_seed", b); item.setdefault("swarm_seed", s); found.append(item)
for key, value in node.items():
key_seed = _seed_from_key(key)
if key_seed in BASE_SEEDS:
found.extend(_collect_cells(value, key_seed, s))
elif key_seed in SWARM_SEEDS:
found.extend(_collect_cells(value, b, key_seed))
elif key not in {"base_seed", "swarm_seed"}:
found.extend(_collect_cells(value, b, s))
elif isinstance(node, list):
for value in node:
found.extend(_collect_cells(value, base, swarm))
return found
def _collect_base_cells(node: Any, base: int | None = None) -> list[dict[str, Any]]:
"""Collect one record per base seed from flattened or base->record schemas."""
found: list[dict[str, Any]] = []
if isinstance(node, Mapping):
b = _int(node, "base_seed") if _int(node, "base_seed") is not None else base
if b is not None and any(k in node for k in ("updates", "gradient_updates", "counters", "metrics", "checkpoint", "endpoint", "objective")):
item = dict(node); item.setdefault("base_seed", b); found.append(item)
for key, value in node.items():
key_seed = _seed_from_key(key)
if key_seed in BASE_SEEDS: found.extend(_collect_base_cells(value, key_seed))
elif key not in {"base_seed", "swarm_seed"}: found.extend(_collect_base_cells(value, b))
elif isinstance(node, list):
for value in node: found.extend(_collect_base_cells(value, base))
return found
def _method_base_cells(result: Mapping[str, Any], method: str) -> list[dict[str, Any]]:
arms = result.get("arms")
return _collect_base_cells(arms.get(method)) if isinstance(arms, Mapping) and method in arms else []
def _method_evidence(value: Any, method: str, family: str, root: Path) -> bool:
"""Require non-empty metric or prediction evidence below an exact method key."""
if isinstance(value, Mapping):
for key, item in value.items():
normalized = str(key).lower().replace("-", "_")
if normalized == method:
if _prediction_records(item, root) is not None: return True
if isinstance(item, Mapping) and (_number(item, "nll", "loss", "accuracy", "map50_95", "map50", "mAP50-95") is not None): return True
if _method_evidence(item, method, family, root): return True
if _method_evidence(item, method, family, root): return True
elif isinstance(value, list):
return bool(value) and any(_method_evidence(item, method, family, root) for item in value)
return False
def _require_confirmation_methods(result: Mapping[str, Any], workload: str, family: str, root: Path, issues: dict[str, list[str]]) -> None:
confirmation = result.get("confirmation")
required = {"feature_pso", "feature_random", "feature_adam", "head_adam"}
required |= ({"uniform", "uniform_temperature", "slsqp_weights", "ensemble_pso"} if family == "classification" else {"uniform_wbf", "ensemble_pso", "ensemble_random"})
for method in sorted(required):
if not _method_evidence(confirmation, method, family, root):
_issue(issues, "metrics", f"{workload}: confirmation lacks predictions/metrics for required method {method}")
def _method_cells(result: Mapping[str, Any], method: str) -> list[dict[str, Any]]:
arms = result.get("arms")
if not isinstance(arms, Mapping):
return []
value = arms.get(method)
return _collect_cells(value) if value is not None else []
def _verify_matrix(result: Mapping[str, Any], workload: str, issues: dict[str, list[str]]) -> dict[str, Any]:
arms = result.get("arms")
if not isinstance(arms, Mapping):
_issue(issues, "schema", f"{workload}: arms must be an object")
arms = {}
required = {"feature_pso", "feature_random", "feature_adam", "head_adam"}
missing = required - set(arms)
if missing:
_issue(issues, "matrix", f"{workload}: missing arms {sorted(missing)}")
stats: dict[str, Any] = {"primary_queries": 0, "primary_random_queries": 0, "ensemble_queries": 0, "primary_samples": 0, "primary_random_samples": 0, "ensemble_samples": 0, "cells": {}}
for method in ("feature_pso", "feature_random"):
cells = _method_cells(result, method)
stats["cells"][method] = len(cells)
expected = len(BASE_SEEDS) * len(SWARM_SEEDS)
if len(cells) != expected:
_issue(issues, "matrix", f"{workload}: {method} requires {expected} base/swarm cells, got {len(cells)}")
seen: set[tuple[int, int]] = set()
for cell in cells:
key = (_int(cell, "base_seed") or -1, _int(cell, "swarm_seed") or -1)
if key in seen or key[0] not in BASE_SEEDS or key[1] not in SWARM_SEEDS:
_issue(issues, "matrix", f"{workload}: invalid or duplicate {method} cell {key}")
seen.add(key)
queries, samples = _record_queries(cell)
if queries != PRIMARY_QUERIES:
_issue(issues, "accounting", f"{workload}: {method} {key} queries must be {PRIMARY_QUERIES}, got {queries}")
if samples != PRIMARY_QUERIES * OBJECTIVE_SAMPLES[workload]:
_issue(issues, "accounting", f"{workload}: {method} {key} samples must be {PRIMARY_QUERIES * OBJECTIVE_SAMPLES[workload]}, got {samples}")
if method == "feature_pso":
if queries is not None: stats["primary_queries"] += queries
if samples is not None: stats["primary_samples"] += samples
else:
if queries is not None: stats["primary_random_queries"] += queries
if samples is not None: stats["primary_random_samples"] += samples
for method in ("feature_adam", "head_adam"):
cells = _method_base_cells(result, method); stats["cells"][method] = len(cells)
if len(cells) != len(BASE_SEEDS): _issue(issues, "matrix", f"{workload}: {method} requires exactly three base cells, got {len(cells)}")
seen = set()
for cell in cells:
seed = _int(cell, "base_seed")
if seed in seen or seed not in BASE_SEEDS: _issue(issues, "matrix", f"{workload}: invalid or duplicate {method} base cell {seed}")
if seed is not None: seen.add(seed)
ensemble = result.get("ensemble")
if not isinstance(ensemble, Mapping):
_issue(issues, "schema", f"{workload}: ensemble must be an object")
ensemble = {}
required_ensemble = ("uniform", "uniform_temperature", "slsqp_weights", "ensemble_pso") if workload in CLASSIFICATION_WORKLOADS else ("uniform_wbf", "ensemble_pso", "ensemble_random")
for method in required_ensemble:
if method not in ensemble or ensemble.get(method) in (None, {}, []): _issue(issues, "matrix", f"{workload}: missing required ensemble method {method}")
ens_pso = ensemble.get("ensemble_pso", ensemble.get("pso"))
cells = _collect_cells(ens_pso) if ens_pso is not None else []
stats["cells"]["ensemble_pso"] = len(cells)
if len(cells) != len(SWARM_SEEDS):
_issue(issues, "matrix", f"{workload}: ensemble_pso requires three swarm cells, got {len(cells)}")
seen_swarm: set[int] = set()
for cell in cells:
seed = _int(cell, "swarm_seed", "seed")
if seed in seen_swarm or seed not in SWARM_SEEDS:
_issue(issues, "matrix", f"{workload}: invalid or duplicate ensemble swarm cell {seed}")
if seed is not None: seen_swarm.add(seed)
queries, samples = _record_queries(cell)
if queries != ENSEMBLE_QUERIES:
_issue(issues, "accounting", f"{workload}: ensemble_pso queries must be {ENSEMBLE_QUERIES}, got {queries}")
if samples != ENSEMBLE_QUERIES * OBJECTIVE_SAMPLES[workload]:
_issue(issues, "accounting", f"{workload}: ensemble_pso samples must be {ENSEMBLE_QUERIES * OBJECTIVE_SAMPLES[workload]}, got {samples}")
if queries is not None: stats["ensemble_queries"] += queries
if samples is not None: stats["ensemble_samples"] += samples
if workload == DETECTION_WORKLOAD:
random_cells = _collect_cells(ensemble.get("ensemble_random")) if ensemble.get("ensemble_random") is not None else []
stats["cells"]["ensemble_random"] = len(random_cells)
if len(random_cells) != len(SWARM_SEEDS): _issue(issues, "matrix", f"{workload}: ensemble_random requires three swarm cells, got {len(random_cells)}")
seen_random = set()
for cell in random_cells:
seed = _int(cell, "swarm_seed", "seed")
if seed in seen_random or seed not in SWARM_SEEDS: _issue(issues, "matrix", f"{workload}: invalid or duplicate ensemble_random seed {seed}")
if seed is not None: seen_random.add(seed)
return stats
def _verify_selection(result: Mapping[str, Any], workload: str, issues: dict[str, list[str]]) -> None:
selection = result.get("development_selection")
if not isinstance(selection, Mapping):
_issue(issues, "selection", f"{workload}: missing development_selection")
return
selected = selection.get("primary", selection.get("feature_pso", selection.get("selected")))
if not isinstance(selected, Mapping):
_issue(issues, "selection", f"{workload}: missing primary selected endpoints")
return
for base in BASE_SEEDS:
entry = selected.get(str(base), selected.get(base))
if not isinstance(entry, Mapping):
_issue(issues, "selection", f"{workload}: no selected endpoint for base seed {base}")
continue
swarm = _int(entry, "swarm_seed", "seed")
generation = _int(entry, "generation", "final_generation")
if swarm not in SWARM_SEEDS:
_issue(issues, "selection", f"{workload}: selected seed {base} has invalid swarm {swarm}")
if generation != PRIMARY_GENERATIONS:
_issue(issues, "selection", f"{workload}: selected endpoint {base} is not final generation 60")
matches = [c for c in _method_cells(result, "feature_pso") if _int(c, "base_seed") == base and _int(c, "swarm_seed") == swarm]
if not matches:
_issue(issues, "selection", f"{workload}: selected endpoint {base}/{swarm} is not a feature_pso cell")
elif entry.get("vector_hash") and matches[0].get("vector_hash") and entry["vector_hash"] != matches[0]["vector_hash"]:
_issue(issues, "selection", f"{workload}: selected vector hash drift for base {base}")
def _prediction_records(value: Any, root: Path) -> list[dict[str, Any]] | None:
try: value = _resolve_json(root, value)
except (OSError, ValueError, json.JSONDecodeError): return None
if isinstance(value, Mapping):
if "probabilities" in value and "targets" in value:
probabilities, targets = value["probabilities"], value["targets"]
for attr in ("detach", "cpu"):
if hasattr(probabilities, attr): probabilities = getattr(probabilities, attr)()
if hasattr(targets, attr): targets = getattr(targets, attr)()
if hasattr(probabilities, "tolist"): probabilities = probabilities.tolist()
if hasattr(targets, "tolist"): targets = targets.tolist()
if isinstance(probabilities, (list, tuple)) and isinstance(targets, (list, tuple)) and len(probabilities) == len(targets):
return [{"probabilities": list(probability), "target": int(target)} for probability, target in zip(probabilities, targets)]
return None
for key in ("records", "predictions", "images", "examples", "data"):
if key in value:
got = _prediction_records(value[key], root)
if got is not None: return got
return None
if isinstance(value, list) and all(isinstance(x, Mapping) for x in value):
return [dict(x) for x in value]
return None
def _find_prediction_sets(value: Any, root: Path, prefix: str = "$") -> dict[str, list[dict[str, Any]]]:
found: dict[str, list[dict[str, Any]]] = {}
if isinstance(value, Mapping):
for key, item in value.items():
name = f"{prefix}.{key}"
if "prediction" in str(key).lower() or str(key).lower() in {"base", "pso", "feature_pso", "test"}:
records = _prediction_records(item, root)
if records is not None: found[name] = records
found.update(_find_prediction_sets(item, root, name))
elif isinstance(value, list) and value and isinstance(value[0], Mapping):
records = _prediction_records(value, root)
if records is not None: found[prefix] = records
return found
def classification_metrics(records: Sequence[Mapping[str, Any]]) -> dict[str, Any]:
"""Recompute unrounded NLL and accuracy from per-image probabilities/targets."""
probs: list[list[float]] = []; targets: list[int] = []
for i, record in enumerate(records):
p = record.get("probabilities", record.get("probs", record.get("prob")))
target = record.get("target", record.get("label", record.get("class_id")))
if not isinstance(p, (list, tuple)) or not p or not isinstance(target, int) or isinstance(target, bool) or target < 0 or target >= len(p):
raise ValueError(f"invalid classification prediction at index {i}")
vals = [float(x) for x in p]
if not all(math.isfinite(x) and x >= 0.0 for x in vals) or not math.isclose(math.fsum(vals), 1.0, rel_tol=1e-6, abs_tol=1e-6):
raise ValueError(f"probabilities must be finite and sum to one at index {i}")
probs.append(vals); targets.append(target)
nll = math.fsum(
-math.log(max(p[t], 1e-300)) for p, t in zip(probs, targets)
) / len(probs)
predictions = [
max(range(len(p)), key=p.__getitem__) for p in probs
]
accuracy = sum(
prediction == target
for prediction, target in zip(predictions, targets)
) / len(probs)
brier = math.fsum(
math.fsum(
(probability - float(index == target)) ** 2
for index, probability in enumerate(p)
)
for p, target in zip(probs, targets)
) / len(probs)
confidences = [p[prediction] for p, prediction in zip(probs, predictions)]
ece = 0.0
for bin_index in range(15):
lower = bin_index / 15.0
upper = (bin_index + 1) / 15.0
members = [
index
for index, confidence in enumerate(confidences)
if lower <= confidence <= upper
if bin_index == 14 or confidence < upper
]
if members:
bin_accuracy = math.fsum(
predictions[index] == targets[index] for index in members
) / len(members)
bin_confidence = math.fsum(
confidences[index] for index in members
) / len(members)
ece += (
abs(bin_accuracy - bin_confidence)
* len(members)
/ len(probs)
)
return {
"n": len(probs),
"nll": nll,
"accuracy": accuracy,
"brier": brier,
"ece15": ece,
"probabilities": probs,
"targets": targets,
}
def _box(record: Mapping[str, Any]) -> tuple[float, float, float, float] | None:
value = record.get("box", record.get("bbox", record.get("xyxy")))
if not isinstance(value, (list, tuple)) or len(value) != 4 or not all(_finite(x) for x in value): return None
x1, y1, x2, y2 = map(float, value)
return (x1, y1, x2, y2) if x2 >= x1 and y2 >= y1 else None
def _iou(a: Sequence[float], b: Sequence[float]) -> float:
x1, y1 = max(a[0], b[0]), max(a[1], b[1]); x2, y2 = min(a[2], b[2]), min(a[3], b[3])
inter = max(0.0, x2 - x1) * max(0.0, y2 - y1)
area_a = max(0.0, a[2] - a[0]) * max(0.0, a[3] - a[1]); area_b = max(0.0, b[2] - b[0]) * max(0.0, b[3] - b[1])
return inter / (area_a + area_b - inter) if area_a + area_b - inter > 0 else 0.0
def _interp_ap_101(recall: Sequence[float], precision: Sequence[float]) -> float:
"""Ultralytics compute_ap: precision envelope and 101-point trapezoid."""
mrec = [0.0, *map(float, recall), 1.0]
mpre = [1.0, *map(float, precision), 0.0]
for i in range(len(mpre) - 2, -1, -1): mpre[i] = max(mpre[i], mpre[i + 1])
values: list[float] = []
for k in range(101):
x = k / 100.0; j = 0
while j + 1 < len(mrec) and mrec[j + 1] <= x: j += 1
if j + 1 >= len(mrec): values.append(mpre[-1]); continue
span = mrec[j + 1] - mrec[j]
values.append(mpre[j] if span <= 0 else mpre[j] + (mpre[j + 1] - mpre[j]) * (x - mrec[j]) / span)
return sum((values[i] + values[i + 1]) * 0.5 / 100.0 for i in range(100))
def detection_metrics(records: Sequence[Mapping[str, Any]], class_count: int | None = None) -> dict[str, Any]:
"""Recompute Ultralytics-style whole-dataset AP at IoU .50:.95.
Matching is performed independently per image. Candidate matches are sorted
by IoU and deduplicated by prediction and ground truth, as in
DetectionValidator.process_batch; AP then uses the pinned 101-point
interpolated trapezoid.
"""
thresholds = [0.50 + 0.05 * i for i in range(10)]
parsed: list[tuple[list[dict[str, Any]], list[dict[str, Any]]]] = []; max_class = -1
for i, image in enumerate(records):
predictions = image.get("predictions", image.get("detections", image.get("pred", [])))
truth = image.get("ground_truth", image.get("targets", image.get("gt", image.get("labels", []))))
if not isinstance(predictions, list) or not isinstance(truth, list): raise ValueError(f"invalid detection image record {i}")
pp: list[dict[str, Any]] = []; gg: list[dict[str, Any]] = []
for item in predictions:
if not isinstance(item, Mapping) or _box(item) is None: raise ValueError(f"invalid detection prediction {i}")
cls = item.get("class_id", item.get("class", item.get("cls"))); score = item.get("score", item.get("confidence", item.get("conf")))
if not isinstance(cls, int) or isinstance(cls, bool) or not _finite(score): raise ValueError(f"invalid detection prediction fields {i}")
pp.append({"box": _box(item), "class_id": cls, "score": float(score)}); max_class = max(max_class, cls)
for item in truth:
if not isinstance(item, Mapping) or _box(item) is None: raise ValueError(f"invalid ground truth {i}")
cls = item.get("class_id", item.get("class", item.get("cls")))
if not isinstance(cls, int) or isinstance(cls, bool): raise ValueError(f"invalid ground truth class {i}")
gg.append({"box": _box(item), "class_id": cls}); max_class = max(max_class, cls)
parsed.append((pp, gg))
present = sorted({g["class_id"] for _, gt in parsed for g in gt})
classes = present if class_count is None else [c for c in range(class_count) if c in present]
if not classes: classes = list(range(class_count or (max_class + 1))) or [0]
aps: dict[str, list[float]] = {}; precision50: list[float] = []; recall50: list[float] = []
for cls in classes:
gt_count = sum(sum(x["class_id"] == cls for x in gt) for _, gt in parsed); class_aps: list[float] = []
for threshold in thresholds:
true_by_image: list[list[bool]] = []
for preds, gt in parsed:
candidates = []
for pi, pred in enumerate(preds):
if pred["class_id"] != cls: continue
for gi, target in enumerate(gt):
if target["class_id"] == cls:
overlap = _iou(pred["box"], target["box"])
if overlap >= threshold: candidates.append((overlap, pi, gi))
candidates.sort(key=lambda x: -x[0]); used_pred: set[int] = set(); used_gt: set[int] = set(); matched: set[int] = set()
for overlap, pi, gi in candidates:
if pi not in used_pred and gi not in used_gt: used_pred.add(pi); used_gt.add(gi); matched.add(pi)
true_by_image.append([i in matched for i in range(len(preds))])
ranked = sorted(((pred["score"], hit) for (preds, _), hits in zip(parsed, true_by_image) for pred, hit in zip(preds, hits) if pred["class_id"] == cls), key=lambda x: -x[0])
tp=[]; fp=[]; ctp=cfp=0
for _, hit in ranked:
ctp += int(hit); cfp += int(not hit); tp.append(ctp); fp.append(cfp)
if gt_count == 0 or not ranked:
# Ultralytics ap_per_class skips classes with no predictions; AP is zero.
class_aps.append(0.0); continue
recalls = [x / gt_count for x in tp]; precisions = [x / max(x + y, 1) for x, y in zip(tp, fp)]
class_aps.append(_interp_ap_101(recalls, precisions))
if threshold == 0.5:
precision50.append(precisions[-1] if precisions else 0.0); recall50.append(recalls[-1] if recalls else 0.0)
aps[str(cls)] = class_aps
map50 = math.fsum(v[0] for v in aps.values()) / len(aps); map5095 = math.fsum(x for values in aps.values() for x in values) / (len(aps) * 10)
return {"per_class_ap": aps, "n": len(records), "map50": map50, "map50_95": map5095,
"precision": math.fsum(precision50) / len(precision50) if precision50 else 0.0,
"recall": math.fsum(recall50) / len(recall50) if recall50 else 0.0,
"ground_truth": sum(len(gt) for _, gt in parsed), "predictions": sum(len(preds) for preds, _ in parsed)}
def _metric_from_record(record: Any, family: str) -> dict[str, float] | None:
if not isinstance(record, Mapping): return None
keys = ("nll", "loss") if family == "classification" else ("map50_95", "map50-95", "mAP50-95", "map5095")
primary = _number(record, *keys); accuracy = _number(record, "accuracy", "acc")
map50 = _number(record, "map50", "mAP50")
if family == "classification" and primary is not None and accuracy is not None: return {"nll": primary, "accuracy": accuracy}
if family == "detection" and primary is not None: return {"map50_95": primary, **({"map50": map50} if map50 is not None else {})}
return None
def _quantile(values: Sequence[float], q: float) -> float:
ordered = sorted(float(x) for x in values)
if not ordered: raise ValueError("cannot quantile an empty sequence")
position = (len(ordered) - 1) * q; lower = int(math.floor(position)); upper = min(lower + 1, len(ordered) - 1)
return ordered[lower] + (ordered[upper] - ordered[lower]) * (position - lower)
def _record_identity(record: Mapping[str, Any], index: int) -> Any:
for key in ("image_id", "id", "key", "filename", "path", "index"):
if key in record: return (key, str(record[key]))
return ("position", index)
def _record_ground_truth(record: Mapping[str, Any]) -> Any:
return record.get("ground_truth", record.get("targets", record.get("gt", record.get("labels", []))))
def _bootstrap_alignment(pairs: Sequence[tuple[Sequence[Mapping[str, Any]], Sequence[Mapping[str, Any]]]], family: str) -> tuple[bool, str]:
if not pairs or any(len(a) != len(b) or not a for a, b in pairs): return False, "incomplete or length-mismatched paired records"
reference_ids = [_record_identity(x, i) for i, x in enumerate(pairs[0][0])]
reference_gt = [_record_ground_truth(x) for x in pairs[0][0]]
for pair_index, (base, pso) in enumerate(pairs):
if [_record_identity(x, i) for i, x in enumerate(base)] != reference_ids or [_record_identity(x, i) for i, x in enumerate(pso)] != reference_ids:
return False, f"pair {pair_index} image IDs/order differ"
if family == "classification":
base_targets = [x.get("target", x.get("label", x.get("class_id"))) for x in base]
pso_targets = [x.get("target", x.get("label", x.get("class_id"))) for x in pso]
ref_targets = [x.get("target", x.get("label", x.get("class_id"))) for x in pairs[0][0]]
if base_targets != ref_targets or pso_targets != ref_targets: return False, f"pair {pair_index} targets differ"
elif [_canonical(_record_ground_truth(x)) for x in base] != [_canonical(x) for x in reference_gt] or [_canonical(_record_ground_truth(x)) for x in pso] != [_canonical(x) for x in reference_gt]:
return False, f"pair {pair_index} ground truth differs"
return True, "aligned"
def _bootstrap_from_records(pairs: Sequence[tuple[Sequence[Mapping[str, Any]], Sequence[Mapping[str, Any]]]], family: str) -> dict[str, Any]:
aligned, reason = _bootstrap_alignment(pairs, family)
if not aligned: return {"available": False, "seed": BOOTSTRAP_SEED, "resamples": BOOTSTRAP_RESAMPLES, "reason": reason}
rng = random.Random(BOOTSTRAP_SEED); stats: list[float] = []
if family == "classification":
parsed = [(classification_metrics(a), classification_metrics(b)) for a, b in pairs]
by_class: dict[int, list[int]] = {}
for i, target in enumerate(parsed[0][0]["targets"]): by_class.setdefault(target, []).append(i)
for _ in range(BOOTSTRAP_RESAMPLES):
# One class-stratified draw is shared by every paired base/PSO model.
indices = [rng.choice(class_indices) for class_indices in by_class.values() for _ in class_indices]
deltas = []
for base, pso in parsed:
b_nll = math.fsum(-math.log(max(base["probabilities"][i][base["targets"][i]], 1e-300)) for i in indices) / len(indices)
p_nll = math.fsum(-math.log(max(pso["probabilities"][i][pso["targets"][i]], 1e-300)) for i in indices) / len(indices)
deltas.append((b_nll - p_nll) / b_nll if b_nll else 0.0)
stats.append(math.fsum(deltas) / len(deltas))
else:
for _ in range(BOOTSTRAP_RESAMPLES):
# One whole-image draw is shared by every paired base/PSO model.
indices = [rng.randrange(len(pairs[0][0])) for _ in pairs[0][0]]
deltas = []
for base, pso in pairs:
b = detection_metrics([base[i] for i in indices])["map50_95"]
p = detection_metrics([pso[i] for i in indices])["map50_95"]
deltas.append(p - b)
stats.append(math.fsum(deltas) / len(deltas))
lo = _quantile(stats, BOOTSTRAP_ALPHA); hi = _quantile(stats, 1.0 - BOOTSTRAP_ALPHA)
return {"available": True, "seed": BOOTSTRAP_SEED, "resamples": BOOTSTRAP_RESAMPLES, "alpha": BOOTSTRAP_ALPHA, "lower": lo, "upper": hi, "statistic": math.fsum(stats) / len(stats), "excludes_zero": lo > 0 or hi < 0}
def _prediction_pairs(result: Mapping[str, Any], root: Path, family: str) -> list[tuple[int, list[dict[str, Any]], list[dict[str, Any]]]]:
"""Find one test base/selected pair per frozen base seed by explicit names."""
sets = _find_prediction_sets(result.get("confirmation", {}), root)
out: list[tuple[int, list[dict[str, Any]], list[dict[str, Any]]]] = []
for seed in BASE_SEEDS:
candidates = [(name, records) for name, records in sets.items() if str(seed) in name]
base = next((records for name, records in candidates if any(x in name.lower() for x in ("base", "frozen"))), None)
pso = next((records for name, records in candidates if any(x in name.lower() for x in ("feature_pso", "selected", "pso")) and "ensemble" not in name.lower()), None)
if base is not None and pso is not None: out.append((seed, base, pso))
return out
def _audit_series(value: Any) -> list[dict[str, Any]]:
found: list[dict[str, Any]] = []
if isinstance(value, Mapping):
for item in value.values(): found.extend(_audit_series(item))
elif isinstance(value, list) and value and all(isinstance(item, Mapping) for item in value):
if any("epoch" in item or "step" in item for item in value) and any(_number(item, "loss", "audit_loss") is not None for item in value):
found.append({"records": value})
else:
for item in value: found.extend(_audit_series(item))
return found
def _plateau_flags(result: Mapping[str, Any], workload: str, family: str, issues: dict[str, list[str]]) -> dict[str, Any]:
baselines = result.get("baselines", {})
if not isinstance(baselines, Mapping): return {"available": False, "passed": False}
per_seed: dict[str, bool] = {}; details: dict[str, Any] = {}
for seed in BASE_SEEDS:
entry = baselines.get(str(seed), baselines.get(seed))
series = _audit_series(entry)
records = series[0]["records"] if series else []
records = records[-11:] if len(records) >= 11 else []
losses = [_number(x, "loss", "audit_loss") for x in records]
metric_keys = ("accuracy", "primary_metric", "selection_accuracy") if family == "classification" else ("map50_95", "mAP50-95", "primary_metric", "selection_metric")
metrics = [_number(x, *metric_keys) for x in records]
loss_ok = len(losses) == 11 and all(x is not None for x in losses)
metric_ok = len(metrics) == 11 and all(x is not None for x in metrics)
if loss_ok:
mean = math.fsum(float(x) for x in losses) / len(losses)
loss_ok = (max(losses) - min(losses)) / max(abs(mean), 1e-12) <= 0.01
if metric_ok: metric_ok = max(metrics) - min(metrics) <= 0.005
passed = bool(loss_ok and metric_ok); per_seed[str(seed)] = passed
details[str(seed)] = {"loss_ok": bool(loss_ok), "metric_ok": bool(metric_ok), "observations": len(records)}
declared = _number(entry, "baseline_plateau") if isinstance(entry, Mapping) else None
if isinstance(entry, Mapping) and "baseline_plateau" in entry and bool(entry["baseline_plateau"]) != passed:
_issue(issues, "plateau", f"{workload}: baseline seed {seed} inflated/incorrect plateau flag")
return {"available": bool(per_seed), "per_seed": per_seed, "details": details, "passed": bool(per_seed) and all(per_seed.values())}
def _workload_gates(result: Mapping[str, Any], workload: str, family: str, root: Path, issues: dict[str, list[str]]) -> dict[str, Any]:
pairs = _prediction_pairs(result, root, family)
per_seed: dict[str, Any] = {}
pair_records: list[tuple[Sequence[Mapping[str, Any]], Sequence[Mapping[str, Any]]]] = []
for seed, base_records, pso_records in pairs:
try:
base = classification_metrics(base_records) if family == "classification" else detection_metrics(base_records, 20)
pso = classification_metrics(pso_records) if family == "classification" else detection_metrics(pso_records, 20)
except (ValueError, ZeroDivisionError) as exc:
_issue(issues, "metrics", f"{workload}: test pair {seed} cannot be recomputed: {exc}"); continue
pair_records.append((base_records, pso_records))
if family == "classification": per_seed[str(seed)] = {"base": {"nll": base["nll"], "accuracy": base["accuracy"]}, "pso": {"nll": pso["nll"], "accuracy": pso["accuracy"]}, "relative_nll_reduction": (base["nll"] - pso["nll"]) / base["nll"], "accuracy_delta": pso["accuracy"] - base["accuracy"]}
else: per_seed[str(seed)] = {"base": {"map50_95": base["map50_95"], "map50": base["map50"]}, "pso": {"map50_95": pso["map50_95"], "map50": pso["map50"]}, "map50_95_delta": pso["map50_95"] - base["map50_95"], "map50_delta": pso["map50"] - base["map50"]}
ci = _bootstrap_from_records(pair_records, family)
# Confirmation predictions and the paired CI are required integrity evidence.
# A numerically negative CI is a valid scientific result; an unavailable CI
# means the frozen/confirmed artifact set is incomplete or tampered.
if len(pairs) != len(BASE_SEEDS):
_issue(issues, "metrics", f"{workload}: required confirmation pairs are incomplete ({len(pairs)}/{len(BASE_SEEDS)})")
if not ci.get("available", False):
_issue(issues, "metrics", f"{workload}: required paired bootstrap unavailable: {ci.get('reason', 'missing prediction evidence')}")
if family == "classification" and per_seed:
reductions = [x["relative_nll_reduction"] for x in per_seed.values()]; acc_deltas = [x["accuracy_delta"] for x in per_seed.values()]
gate = len(reductions) == 3 and math.fsum(reductions) / 3 >= 0.01 and math.fsum(acc_deltas) / 3 >= -0.002 and min(acc_deltas) >= -0.005 and sum(x > 0 for x in reductions) >= 2 and ci.get("excludes_zero", False)
elif family == "detection" and per_seed:
deltas = [x["map50_95_delta"] for x in per_seed.values()]
gate = len(deltas) == 3 and math.fsum(deltas) / 3 >= 0.005 and min(deltas) >= -0.005 and sum(x > 0 for x in deltas) >= 2 and ci.get("excludes_zero", False)
else: gate = False
if len(pairs) != len(BASE_SEEDS): _issue(issues, "bootstrap", f"{workload}: missing complete official-test base/feature_pso prediction pairs ({len(pairs)}/3)")
return {"available": bool(per_seed), "per_seed": per_seed, "bootstrap": ci, "generalization_pass": bool(gate)}
def _verify_hashes(result: Mapping[str, Any], root: Path, issues: dict[str, list[str]], workload: str) -> None:
declared = result.get("artifact_hashes")
if not isinstance(declared, Mapping):
_issue(issues, "schema", f"{workload}: missing artifact_hashes")
return
for name, expected in declared.items():
if not isinstance(name, str) or not isinstance(expected, str):
_issue(issues, "seal", f"{workload}: malformed artifact hash entry")
continue
path = (root / name).resolve()
if root.resolve() not in path.parents or not path.is_file():
_issue(issues, "seal", f"{workload}: missing/escaping artifact {name}")
elif _sha256(path) != expected:
_issue(issues, "seal", f"{workload}: artifact hash drift {name}")
def _compare_development_snapshot(development: Any, current: Mapping[str, Any], workload: str, issues: dict[str, list[str]]) -> None:
"""Ensure confirmation did not alter any sealed development decision/state."""
if not isinstance(development, Mapping):
_issue(issues, "schema", f"{workload}: development_result.json must contain an object")
return
development_leakage = development.get("leakage_counters")
if not isinstance(development_leakage, Mapping):
_issue(issues, "leakage", f"{workload}: development snapshot lacks leakage_counters")
else:
loaded = development_leakage.get("official_test_data_loaded_before_freeze", development_leakage.get("test_data_loaded_before_freeze"))
evaluated = development_leakage.get("official_test_evaluations_before_freeze", development_leakage.get("test_evaluations_before_freeze", development_leakage.get("official_test_forward_passes_before_freeze")))
if loaded is not False or evaluated != 0:
_issue(issues, "leakage", f"{workload}: development snapshot records pre-freeze official-test exposure")
for key in ("official_test_construction", "official_test_dataset_construction", "official_test_forward_passes", "official_test_evaluations"):
if key in development_leakage and development_leakage[key] != 0:
_issue(issues, "leakage", f"{workload}: development snapshot {key} must be zero")
development_confirmation = development.get("confirmation")
if development_confirmation not in ({}, None):
_issue(issues, "leakage", f"{workload}: development snapshot confirmation must be empty")
fields = ("config", "manifests", "provenance", "baselines", "arms", "ensemble", "development_selection", "integrity", "resource_ledger", "artifact_hashes")
for field in fields:
if field not in development:
_issue(issues, "seal", f"{workload}: development_result.json missing sealed field {field}")
continue
if field not in current:
_issue(issues, "seal", f"{workload}: current result missing sealed field {field}")
continue
if field == "artifact_hashes":
# Confirmation may add test prediction hashes. Every development hash
# must nevertheless remain present and byte-identical.
old_hashes = development[field]; new_hashes = current[field]
if not isinstance(old_hashes, Mapping) or not isinstance(new_hashes, Mapping):
_issue(issues, "seal", f"{workload}: artifact_hashes changed shape across confirmation")
else:
for name, value in old_hashes.items():
if new_hashes.get(name) != value: _issue(issues, "seal", f"{workload}: sealed artifact hash drift for {name}")
elif field == "integrity":
old_integrity = development[field]; new_integrity = current[field]
if not isinstance(old_integrity, Mapping) or not isinstance(new_integrity, Mapping):
if old_integrity != new_integrity: _issue(issues, "seal", f"{workload}: pre-confirmation integrity changed")
else:
for name, value in old_integrity.items():
if new_integrity.get(name) != value: _issue(issues, "seal", f"{workload}: pre-confirmation integrity field changed: {name}")
elif _canonical(development[field]) != _canonical(current[field]):
_issue(issues, "seal", f"{workload}: sealed pre-confirmation field changed: {field}")
def _evaluate_workload(result: Any, root: Path, workload: str, issues: dict[str, list[str]], development: Any = None) -> dict[str, Any]:
if not isinstance(result, Mapping):
_issue(issues, "schema", f"{workload}: result must be an object"); return {"workload_id": workload, "valid": False}
if result.get("workload_id") != workload: _issue(issues, "matrix", f"{workload}: workload_id mismatch")
if development is not None: _compare_development_snapshot(development, result, workload, issues)
family = "detection" if workload == DETECTION_WORKLOAD else "classification"
if result.get("family") != family: _issue(issues, "matrix", f"{workload}: family must be {family}")
for key in ("manifests", "provenance", "baselines", "arms", "ensemble", "development_selection", "confirmation", "integrity", "leakage_counters", "resource_ledger", "artifact_hashes"):
if key not in result: _issue(issues, "schema", f"{workload}: missing top-level {key}")
_config_checks(result.get("config"), issues, workload); _check_leakage(result, issues, workload); _verify_hashes(result, root, issues, workload)
accounting = _verify_matrix(result, workload, issues); _verify_selection(result, workload, issues); _require_confirmation_methods(result, workload, family, root, issues)
if result.get("integrity", {}).get("confirmed") is False if isinstance(result.get("integrity"), Mapping) else False:
_issue(issues, "seal", f"{workload}: integrity declares confirmation failure")
prediction_sets = _find_prediction_sets(result.get("confirmation", {}), root)
recomputed: dict[str, Any] = {}
for name, records in prediction_sets.items():
try: recomputed[name] = classification_metrics(records) if family == "classification" else detection_metrics(records, 20)
except (ValueError, ZeroDivisionError) as exc: _issue(issues, "metrics", f"{workload}: invalid stored predictions at {name}: {exc}")
# Check every explicitly stored metric that has a corresponding recomputation.
for name, metric in recomputed.items():
if "pso" in name.lower() and isinstance(metric, Mapping):
stored = _metric_from_record(result.get("confirmation"), family)
if stored and family == "classification" and not (_same(stored.get("nll"), metric.get("nll"), 1e-7) and _same(stored.get("accuracy"), metric.get("accuracy"), 1e-7)):
_issue(issues, "metrics", f"{workload}: stored classification metric disagrees with probabilities at {name}")
plateau = _plateau_flags(result, workload, family, issues)
gates = _workload_gates(result, workload, family, root, issues)
objective = _number(result.get("development_selection"), "objective_improvement", "relative_objective_improvement")
selection_metric = _number(result.get("development_selection"), "selection_metric", "selection_nll", "selection_map50_95")
overfit = bool(objective is not None and objective >= 0.01 and selection_metric is not None and ((family == "classification" and selection_metric > 0.01) or (family == "detection" and selection_metric < -0.005)))
if overfit: _issue(issues, "overfit", f"{workload}: objective improvement conflicts with held-out/selection metric")
return {"workload_id": workload, "family": family, "valid": True, "accounting": accounting, "recomputed": recomputed, "plateau": plateau, "gates": gates, "overfit_signal": overfit}
def evaluate_run(run_root: str | os.PathLike[str]) -> dict[str, Any]:
"""Evaluate one frozen run, returning findings even when artifacts are malformed."""
root = Path(run_root); issues = {key: [] for key in ISSUE_CATEGORIES}; workloads: dict[str, Any] = {}
manifest: Any = None
try: manifest = _json(root / "frozen_manifest.json")
except (OSError, ValueError, json.JSONDecodeError) as exc: _issue(issues, "schema", f"cannot load frozen_manifest.json: {exc}")
if isinstance(manifest, Mapping):
for location in _walk_nonfinite(manifest): _issue(issues, "finite", f"non-finite value in frozen manifest at {location}")
_hash_manifest(root, manifest, issues); _config_checks(manifest.get("config"), issues)
frozen_config = manifest.get("config") if isinstance(manifest.get("config"), Mapping) else {}
if sorted(frozen_config.get("workload_ids", ())) != sorted(WORKLOADS): _issue(issues, "matrix", "frozen manifest does not seal all three workloads")
for workload in WORKLOADS:
path = root / "workloads" / workload / "result.json"
development_path = root / "workloads" / workload / "development_result.json"
if isinstance(manifest, Mapping):
manifest_artifacts = manifest.get("artifacts", {})
expected_development = f"workloads/{workload}/development_result.json"
if not isinstance(manifest_artifacts, Mapping) or expected_development not in manifest_artifacts:
_issue(issues, "seal", f"{workload}: frozen manifest must seal {expected_development}")
try: result = _json(path)
except (OSError, ValueError, json.JSONDecodeError) as exc:
_issue(issues, "schema", f"{workload}: cannot load result.json: {exc}"); continue
try: development = _json(development_path)
except (OSError, ValueError, json.JSONDecodeError) as exc:
_issue(issues, "seal", f"{workload}: cannot load development_result.json: {exc}"); development = None
for location in _walk_nonfinite(result): _issue(issues, "finite", f"{workload}: non-finite value at {location}")
if development is not None:
for location in _walk_nonfinite(development): _issue(issues, "finite", f"{workload}: non-finite development value at {location}")
workloads[workload] = _evaluate_workload(result, root, workload, issues, development)
# Cross-workload exact accounting is intentionally independent of stored totals.
totals = {key: sum(int(w.get("accounting", {}).get(key, 0)) for w in workloads.values()) for key in ("primary_queries", "primary_random_queries", "ensemble_queries", "primary_samples", "primary_random_samples", "ensemble_samples")}
totals["pso_queries"] = totals["primary_queries"] + totals["ensemble_queries"]
totals["candidate_samples"] = totals["primary_samples"] + totals["ensemble_samples"]
if totals["pso_queries"] != TOTAL_PSO_QUERIES: _issue(issues, "accounting", f"total PSO queries must be {TOTAL_PSO_QUERIES}, got {totals['pso_queries']}")
if totals["candidate_samples"] != TOTAL_CANDIDATE_SAMPLES: _issue(issues, "accounting", f"total candidate-sample evaluations must be {TOTAL_CANDIDATE_SAMPLES}, got {totals['candidate_samples']}")
# A success flag is never consumed; it is checked against independently observed integrity.
integrity_ok = not any(issues[key] for key in ("schema", "provenance", "matrix", "accounting", "seal", "selection", "finite", "leakage", "metrics"))
payload = {"evaluator_version": EVALUATOR_VERSION, "protocol_version": PROTOCOL_VERSION, "run_root": str(root), "pass": integrity_ok, "integrity_pass": integrity_ok, "workloads": workloads, "accounting": {**totals, "expected_pso_queries": TOTAL_PSO_QUERIES, "expected_candidate_samples": TOTAL_CANDIDATE_SAMPLES}, "issues": issues, "issue_counts": {key: len(value) for key, value in issues.items()}}
return payload
def build_cli_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="Evaluate frozen post-training model-convergence artifacts")
parser.add_argument("--run-root", type=Path, required=True)
parser.add_argument("--output", type=Path, default=None)
return parser
def main(argv: Sequence[str] | None = None) -> int:
args = build_cli_parser().parse_args(argv); payload = evaluate_run(args.run_root)
destination = args.output or args.run_root / "evaluation.json"
try: _atomic_json(destination, payload)
except OSError as exc:
print(f"evaluator output failed: {exc}", file=sys.stderr); return 2
print(json.dumps(payload, sort_keys=True, separators=(",", ":")))
return 0 if payload["pass"] else 1
__all__ = [
"BASE_SEEDS", "BOOTSTRAP_ALPHA", "BOOTSTRAP_RESAMPLES", "BOOTSTRAP_SEED", "CLASSIFICATION_WORKLOADS",
"DETECTION_WORKLOAD", "EVALUATOR_VERSION", "ENSEMBLE_QUERIES", "TOTAL_CANDIDATE_SAMPLES", "TOTAL_PSO_QUERIES",
"WORKLOADS", "classification_metrics", "detection_metrics", "evaluate_run", "main", "build_cli_parser",
]
if __name__ == "__main__":
raise SystemExit(main())