scripts/evaluate_rgbd_volume_abstention.py
#!/usr/bin/env python3
"""V13: abstain outside the state's volume (no training).
The frozen V11 C1 selected checkpoints render depth and opacity for queries far from the context
frames (EVAL-FAR) and for the near queries of EVAL-V3/V4. AHFILL8 uses REPROJ_HARMONIC on NEAR
pixels and on FAR pixels whose rendered opacity is below 0.5, the carrier elsewhere, as fixed by
PROTOCOL.md. Stages: `lock` freezes the protocol, code and inputs before any far query is rendered;
`seal --cohort` replays two sealed EVAL-V3 scenes, then renders and hashes every prediction and
geometric baseline of one cohort before any query depth is read; `score` needs every cohort's seal;
`analyze` computes the preregistered contrasts.
"""
# ruff: noqa: E501 -- Chinese scientific report prose
from __future__ import annotations
import argparse
import datetime
import functools
import hashlib
import importlib.util
import json
from pathlib import Path
import numpy as np
import torch
from mcss.dynamic.types import hash_scene_state
from mcss.geometry import generate_rays, intersect_aabb, transform_cameras
from mcss.measurements import FixedMeasurementRenderer
from mcss.mechanism_pilot import rgbd_completion_carrier as v11_carrier
from mcss.mechanism_pilot.readout_reanalysis import depth_metrics
from mcss.mechanism_pilot.rgbd_depth_bounds import context_depth_bounds
from mcss.types import Cameras
EXPERIMENT = "EXP-3D-RGBD-VOLUME-V13"
V11 = Path("outputs/EXP-3D-RGBD-COMPLETION-V11")
INPAINT = Path("outputs/EXP-3D-RGBD-INPAINT-BASELINES-V1")
EVAL_FAR = Path("outputs/EXP-3D-RGBD-EVAL-FAR-DATA")
EXPLORATORY = "audit/exploratory_opacity_fallback"
COHORTS = {
"EVAL_FAR": (EVAL_FAR / "manifest_eval_far.json", "far_query", "EVAL_FAR"),
"EVAL_V3_NEAR": (
Path("outputs/EXP-3D-RGBD-EVAL-V3-DATA/manifest_eval_v3.json"),
"primary_query",
"EVAL_V3",
),
"EVAL_V4_NEAR": (
Path("outputs/EXP-3D-RGBD-EVAL-V4-DATA/manifest_eval_v4.json"),
"primary_query",
"EVAL_V4",
),
}
PRIMARY = "EVAL_FAR"
THRESHOLD = 0.5
DESCRIPTIVE_THRESHOLDS = (0.3, 0.7, 0.9)
REGIONS = ("ALL", "NEAR", "FAR")
REPLAY_SCENES = 2
METHODS = (
"CONSTANT",
"REPROJ_NN",
"REPROJ_HARMONIC",
"C1",
"HFILL8",
"AHFILL8",
*(f"AHFILL8_t{t}" for t in DESCRIPTIVE_THRESHOLDS),
)
CONTRASTS = {
"ABSTAIN_GAIN": ("HFILL8", "ALL", "AHFILL8", "ALL"),
"HARMONIC_VS_AHFILL8": ("REPROJ_HARMONIC", "ALL", "AHFILL8", "ALL"),
"HARMONIC_VS_HFILL8": ("REPROJ_HARMONIC", "ALL", "HFILL8", "ALL"),
"NN_VS_AHFILL8": ("REPROJ_NN", "ALL", "AHFILL8", "ALL"),
"CONSTANT_VS_C1": ("CONSTANT", "ALL", "C1", "ALL"),
"NN_VS_C1": ("REPROJ_NN", "ALL", "C1", "ALL"),
"HARMONIC_VS_C1_FAR": ("REPROJ_HARMONIC", "FAR", "C1", "FAR"),
"HARMONIC_VS_AHFILL8_FAR": ("REPROJ_HARMONIC", "FAR", "AHFILL8", "FAR"),
**{
f"HARMONIC_VS_AHFILL8_t{t}": ("REPROJ_HARMONIC", "ALL", f"AHFILL8_t{t}", "ALL")
for t in DESCRIPTIVE_THRESHOLDS
},
}
SCRIPTS = Path(__file__).resolve().parent
def sha(path):
return hashlib.sha256(Path(path).read_bytes()).hexdigest()
def write(path, value):
path = Path(path)
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(value, indent=2, sort_keys=True, allow_nan=False) + "\n")
def read(path):
return json.loads(Path(path).read_text())
def module(name, filename):
spec = importlib.util.spec_from_file_location(name, SCRIPTS / filename)
loaded = importlib.util.module_from_spec(spec)
spec.loader.exec_module(loaded)
return loaded
@functools.cache
def host():
"""V11's frozen EVAL-V3 module: context loader, aggregation, bootstrap and V2 gate."""
return module("v13_v11_eval", "evaluate_rgbd_completion_eval_v3.py")
@functools.cache
def inpainting():
"""INPAINT-V1's frozen module: ray cosine, harmonic filling, NEAR mask, label rule."""
return module("v13_inpaint", "run_rgbd_inpaint_baselines.py")
def abstain(near, opacity, harmonic, carrier, threshold):
"""AHFILL8: harmonic on NEAR pixels and on FAR pixels the carrier renders with low opacity."""
return np.where(near | (opacity < threshold), harmonic, carrier)
def inputs(root, v11=V11, inpaint=INPAINT):
paths = {
"protocol": root / "PROTOCOL.md",
"script": Path(__file__).resolve(),
"v11_eval_script": SCRIPTS / "evaluate_rgbd_completion_eval_v3.py",
"inpaint_script": SCRIPTS / "run_rgbd_inpaint_baselines.py",
"geometric_script": SCRIPTS / "run_rgbd_geometric_baselines.py",
"v11_contract": v11 / "training_contract.json",
"v11_selected": v11 / "selected_checkpoints.json",
"v11_eval_v3_seal": v11 / "raw/eval_v3/prediction_seal.json",
"inpaint_lock": inpaint / "lock.json",
"eval_far_candidate_lock": EVAL_FAR / "candidate_lock.json",
"eval_far_integrity": EVAL_FAR / "preparation_integrity.json",
}
for name, (manifest, _, _) in COHORTS.items():
paths[f"{name}_manifest"] = manifest
for cohort in ("EVAL_V3", "EVAL_V4", "REPLICA"):
paths[f"exploratory_{cohort}"] = root / EXPLORATORY / f"fallback_{cohort}.json"
return paths
# ---------------------------------------------------------------- stages
def lock(root, v11=V11, inpaint=INPAINT):
root = Path(root).resolve()
if root.name != EXPERIMENT:
raise PermissionError("Exact experiment directory required")
if (root / "lock.json").exists():
raise FileExistsError("Never re-lock")
if not (root / "PROTOCOL.md").is_file():
raise PermissionError("Protocol text required before locking")
if (root / "raw").exists():
raise PermissionError("No prediction may exist before the lock")
if read(EVAL_FAR / "preparation_integrity.json")["status"] != "PASS":
raise PermissionError("EVAL-FAR data must be prepared with integrity PASS")
paths = inputs(root, v11, inpaint)
write(
root / "lock.json",
{
"status": "FROZEN_BEFORE_ANY_FAR_QUERY_PREDICTION",
"experiment": EXPERIMENT,
"created_utc": datetime.datetime.now(datetime.UTC).isoformat(),
"threshold": THRESHOLD,
"primary": "EVAL_FAR: P1 AbsRel(HFILL8) - AbsRel(AHFILL8) (frozen V2 gate); P2 AbsRel(REPROJ_HARMONIC) - AbsRel(AHFILL8) (INPAINT-V1 label rule)",
"input_sha256": {
name: {"path": str(Path(p).resolve()), "sha256": sha(p)}
for name, p in paths.items()
},
"far_query_rendered_before_lock": False,
"final_holdout_opened": False,
},
)
print("LOCKED", EXPERIMENT, flush=True)
def verify(root, v11=V11):
lock_ = read(root / "lock.json")
for name, entry in lock_["input_sha256"].items():
if sha(entry["path"]) != entry["sha256"]:
raise PermissionError(f"Locked input changed: {name}")
if read(v11 / "integrity.json")["status"] != "PASS":
raise PermissionError("V11 must be finalized with integrity PASS")
return lock_
def carriers(v11=V11):
config = read(v11 / "training_contract.json")
selected = read(v11 / "selected_checkpoints.json")["C1"]
models = {}
for seed in config["seeds"]:
item = selected[str(seed)]
if sha(Path(item["path"])) != item["sha256"]:
raise PermissionError("Selected V11 C1 checkpoint changed")
models[seed] = v11_carrier.load_checkpoint(item["path"], "cpu")[0].eval()
return config, models
@torch.no_grad()
def predict(record, manifest, base, models, constant, role_name, access, tag, states=None):
"""Every sealed array of every query of one scene: carrier depth and opacity, box, geometry.
`tag` only names the construction episode ("eval_v3" reproduces V11's own episode ids).
"""
stage, inp = host(), inpainting()
geo = stage.geometry()
renderer = FixedMeasurementRenderer(n_samples=64, ray_chunk_size=2048)
out, sid = {}, record["scene_id"]
for role, context in stage.context_of(record, manifest, base, access).items():
anchor = context[0].camera.c2w
bounds = context_depth_bounds(context, anchor)
points = geo.context_points(context)
built = {}
for seed, model in models.items():
state, state_anchor = v11_carrier.build_state(
model, context, bounds, f"{tag}:{sid}:{role}"
)
built[seed] = (state, state_anchor)
if states is not None:
states[f"C1:{seed}:{sid}/{role}"] = hash_scene_state(state)
box_state, box_anchor = next(iter(built.values()))
if not all(
torch.equal(s.bounds, box_state.bounds) and torch.equal(a, box_anchor)
for s, a in built.values()
):
raise AssertionError("All seeds of one context must share the volume and the anchor")
for qid in record["roles"][role_name]:
access.append({"scene_id": sid, "frame_id": qid, "purpose": "QUERY_CAMERA_ONLY"})
camera = geo.query_camera(record, qid, manifest["image_size"])
depth, hit = geo.reproject(points, camera)
reproj = geo.fill_nearest(depth, hit, constant) if hit.any() else None
if reproj is None:
reproj = np.full(camera.image_size, constant, dtype=np.float64)
arrays = {
"REPROJ_NN": reproj,
"hit": hit,
"near": inp.near_mask(hit),
"REPROJ_HARMONIC": inp.harmonic_fill(depth, hit, inp.ray_cosine(camera), constant),
}
batched = Cameras(
camera.intrinsics[None, None], camera.c2w[None, None], camera.image_size
)
box_local = transform_cameras(batched, torch.linalg.inv(box_anchor))
origins, directions = generate_rays(box_local.to(dtype=torch.float64))
t_near, t_exit, box_hit = intersect_aabb(
origins[0, 0], directions[0, 0], box_state.bounds[0].double()
)
arrays |= {
"BOX_NEAR": t_near.numpy(),
"BOX_EXIT": t_exit.numpy(),
"BOX_HIT": box_hit.numpy(),
}
for seed, (state, state_anchor) in built.items():
local = transform_cameras(batched, torch.linalg.inv(state_anchor))
pred = renderer(state, local, ("depth", "visibility"))
arrays[f"C1_{seed}"] = pred["depth"][0, 0, 0].numpy().astype(np.float64)
arrays[f"OPACITY_{seed}"] = pred["visibility"][0, 0, 0].numpy()
out[role, qid] = arrays
return out
def replay(models, constant, v11=V11, inpaint=INPAINT):
"""Our code path on the first EVAL-V3 scenes must reproduce V11's and INPAINT-V1's sealed files."""
manifest_path, role_name, _ = COHORTS["EVAL_V3_NEAR"]
manifest = read(manifest_path)
seal_ = read(v11 / "raw/eval_v3/prediction_seal.json")["predictions"]
inpaint_seal = read(inpaint / "raw/inpaint_seal.json")
worst, compared = 0.0, 0
for record in manifest["scenes"][:REPLAY_SCENES]:
sid = record["scene_id"]
mine = predict(
record, manifest, manifest_path.parent, models, constant, role_name, [], "eval_v3"
)
for (role, qid), arrays in mine.items():
sealed = dict(np.load(seal_[f"{sid}/{role}/{qid}"]["path"]))
filled = dict(np.load(inpaint_seal[f"EVAL_V3/{sid}/{role}/{qid}"]["path"]))
if not (
np.array_equal(arrays["hit"], sealed["hit"])
and np.array_equal(arrays["near"], sealed["near"])
):
raise AssertionError(f"Replay hit/near mask differs for {sid}/{role}/{qid}")
pairs = [
(arrays["REPROJ_NN"], sealed["REPROJ_NN"]),
(arrays["REPROJ_HARMONIC"], filled["REPROJ_HARMONIC"]),
]
pairs += [(arrays[f"C1_{seed}"], sealed[f"C1_{seed}"]) for seed in models]
for a, b in pairs:
worst = max(worst, float(np.max(np.abs(a - b))))
compared += 1
if worst > 1e-6:
raise AssertionError(f"Replay of the sealed EVAL-V3 pipeline failed: {worst}")
return {"scenes": REPLAY_SCENES, "arrays_compared": compared, "max_abs_deviation": worst}
def seal(root, cohort, v11=V11, inpaint=INPAINT):
root = Path(root).resolve()
verify(root, v11)
out = root / "raw" / cohort
if (out / "prediction_seal.json").exists():
raise FileExistsError("Each cohort is sealed once")
if (root / "raw" / "query_rows.json").exists():
raise PermissionError("No cohort may be sealed after scoring")
config, models = carriers(v11)
constant = config["eval_v3"]["reprojection_fill_constant_m"]
if config["eval_v3"]["near_px"] != inpainting().NEAR_PX:
raise PermissionError("NEAR definition must be V11's")
manifest_path, role_name, split = COHORTS[cohort]
manifest = read(manifest_path)
if any(r["split"] != split for r in manifest["scenes"]):
raise PermissionError(f"{cohort}: {split} records only")
replayed = replay(models, constant, v11, inpaint)
print("REPLAY_OK", cohort, json.dumps(replayed), flush=True)
access, entries, states = [], {}, {}
for number, record in enumerate(manifest["scenes"], 1):
sid = record["scene_id"]
scene = predict(
record,
manifest,
manifest_path.parent,
models,
constant,
role_name,
access,
cohort.lower(),
states,
)
for (role, qid), arrays in scene.items():
path = out / "predictions" / f"{sid}_{role}_{qid}.npz"
path.parent.mkdir(parents=True, exist_ok=True)
np.savez_compressed(path, **arrays)
entries[f"{sid}/{role}/{qid}"] = {"path": str(path), "sha256": sha(path)}
print("SEALED_SCENE", cohort, number, len(manifest["scenes"]), sid, flush=True)
if any(
"depth" in e.get("channels", []) and e.get("purpose") != "CONTEXT_ONLY_RGBD" for e in access
):
raise AssertionError("Only context depth may be read before the seal")
write(out / "state_hashes.json", states)
write(out / "GT_access_before_seal.json", access)
write(
out / "prediction_seal.json",
{
"cohort": cohort,
"role": role_name,
"predictions": entries,
"state_hashes_sha256": sha(out / "state_hashes.json"),
"query_depth_read": False,
"near_px": config["eval_v3"]["near_px"],
"reprojection_fill_constant_m": constant,
"replay_on_eval_v3": replayed,
},
)
print("PREDICTIONS_SEALED", cohort, len(entries), flush=True)
def absrel(pred, gt, mask):
return float(np.mean(np.abs(pred[mask] - gt[mask]) / gt[mask])) if mask.any() else None
def score(root):
root = Path(root).resolve()
lock_ = verify(root)
seals = {c: read(root / "raw" / c / "prediction_seal.json") for c in COHORTS}
seeds = read(lock_["input_sha256"]["v11_contract"]["path"])["seeds"]
rows, flags, access = [], [], []
for cohort, (manifest_path, role_name, _) in COHORTS.items():
seal_ = seals[cohort]
constant = seal_["reprojection_fill_constant_m"]
for record in read(manifest_path)["scenes"]:
sid = record["scene_id"]
frames = {f["frame_id"]: f for f in record["frames"]}
for role in ("A", "B"):
for qid in record["roles"][role_name]:
entry = seal_["predictions"][f"{sid}/{role}/{qid}"]
if sha(entry["path"]) != entry["sha256"]:
raise PermissionError("A sealed prediction changed")
arrays = dict(np.load(entry["path"]))
access.append(
{
"cohort": cohort,
"scene_id": sid,
"frame_id": qid,
"purpose": "POST_SEAL_QUERY_DEPTH",
}
)
gt = np.load(frames[qid]["depth"]).squeeze().astype(np.float64)
valid = np.isfinite(gt) & (gt > 0)
near = arrays["near"].astype(bool)
far = valid & ~near
harmonic = arrays["REPROJ_HARMONIC"]
inside = (
arrays["BOX_HIT"].astype(bool)
& (gt >= arrays["BOX_NEAR"])
& (gt <= arrays["BOX_EXIT"])
)
masks = {"ALL": valid, "NEAR": valid & near, "FAR": far}
predictions = {
("CONSTANT", 0): np.full(gt.shape, constant, dtype=np.float64),
("REPROJ_NN", 0): arrays["REPROJ_NN"],
("REPROJ_HARMONIC", 0): harmonic,
}
for seed in seeds:
carrier = arrays[f"C1_{seed}"]
opacity = arrays[f"OPACITY_{seed}"].astype(np.float64)
predictions["C1", seed] = carrier
predictions["HFILL8", seed] = np.where(near, harmonic, carrier)
predictions["AHFILL8", seed] = abstain(
near, opacity, harmonic, carrier, THRESHOLD
)
for t in DESCRIPTIVE_THRESHOLDS:
predictions[f"AHFILL8_t{t}", seed] = abstain(
near, opacity, harmonic, carrier, t
)
flagged = far & (opacity < THRESHOLD)
flags.append(
{
"cohort": cohort,
"scene_id": sid,
"role": role,
"query_id": qid,
"seed": seed,
"surface_in_volume": float(inside[valid].mean()),
"far_fraction": float(far.sum() / max(valid.sum(), 1)),
"flagged_share_of_far": float(flagged.sum() / max(far.sum(), 1)),
"flagged": float(flagged.sum()),
"flagged_outside": float((flagged & ~inside).sum()),
"far_outside": float((far & ~inside).sum()),
}
)
for (method, seed), value in predictions.items():
row = {
"cohort": cohort,
"scene_id": sid,
"role": role,
"query_id": qid,
"method": method,
"seed": seed,
}
for region, mask in masks.items():
row[f"absrel_{region}"] = absrel(value, gt, mask)
row["depth_absrel"] = depth_metrics(value, gt)["depth_absrel"]
row["far_fraction"] = float(far.sum() / max(valid.sum(), 1))
rows.append(row)
write(root / "raw" / "query_rows.json", rows)
write(root / "raw" / "flag_rows.json", flags)
write(root / "raw" / "GT_access_after_seal.json", access)
print("SCORED", len(rows), flush=True)
return rows
def cohort_block(rows, flags):
stage, label = host(), inpainting().geometry().label
def values(method, region):
return stage.scene_values(rows, method, f"absrel_{region}")
methods = sorted({r["method"] for r in rows})
block = {
"absolute_absrel": {
m: {region: stage.summary(values(m, region)) for region in REGIONS} for m in methods
},
"n_scenes": len({r["scene_id"] for r in rows}),
"contrasts": {},
}
for name, (a, ra, b, rb) in CONTRASTS.items():
stats = stage.contrast(values(a, ra), values(b, rb))
entry = {"gain": stats, "definition": f"AbsRel {ra}({a}) - AbsRel {rb}({b})"}
if stats is not None:
entry["label"] = label(stats)
entry["gate"], entry["gate_checks"] = stage.gate(stats)
block["contrasts"][name] = entry
per_scene = {}
for f in flags:
per_scene.setdefault(f["scene_id"], []).append(f)
def scene_mean(key):
return float(np.mean([np.mean([f[key] for f in v]) for v in per_scene.values()]))
pooled = {
key: sum(f[key] for f in flags) for key in ("flagged", "flagged_outside", "far_outside")
}
block["coverage"] = {
"surface_in_volume": scene_mean("surface_in_volume"),
"far_fraction": scene_mean("far_fraction"),
"flagged_share_of_far": scene_mean("flagged_share_of_far"),
"precision_outside_volume": pooled["flagged_outside"] / max(pooled["flagged"], 1),
"recall_outside_volume": pooled["flagged_outside"] / max(pooled["far_outside"], 1),
}
return block
def analyze_rows(rows, flags):
result = {"cohorts": {}}
for cohort in COHORTS:
subset = [r for r in rows if r["cohort"] == cohort]
if subset:
result["cohorts"][cohort] = cohort_block(
subset, [f for f in flags if f["cohort"] == cohort]
)
primary = result["cohorts"][PRIMARY]["contrasts"]
p1 = primary["ABSTAIN_GAIN"].get("gate", "UNDEFINED")
p2 = primary["HARMONIC_VS_AHFILL8"].get("label", "UNDEFINED")
first, second = p1 == "SUPPORTED", p2 == "ABOVE"
result |= {
"ABSTAIN_STATUS": p1,
"HARMONIC_LABEL": p2,
"INTERPRETATION_BRANCH": "A" if first and second else "B" if first or second else "C",
}
return result
def analyze(root):
root = Path(root).resolve()
rows_path, flags_path = root / "raw" / "query_rows.json", root / "raw" / "flag_rows.json"
result = analyze_rows(read(rows_path), read(flags_path))
result["replay_on_eval_v3"] = {
c: read(root / "raw" / c / "prediction_seal.json")["replay_on_eval_v3"] for c in COHORTS
}
result["input_sha256"] = {str(p): sha(p) for p in (rows_path, flags_path)}
result["final_holdout_opened"] = False
write(root / "volume_results.json", result)
report(root, result)
return result
def fmt(stats):
if not stats:
return "UNDEFINED"
lo, hi = stats["ci95"]
return f"{stats['mean']:+.4f} [{lo:+.4f}, {hi:+.4f}],场景 {stats['positive']}/{stats['tied']}/{stats['negative']}"
def report(root, result):
primary = result["cohorts"][PRIMARY]["contrasts"]
worst = max(r["max_abs_deviation"] for r in result["replay_on_eval_v3"].values())
lines = [
f"# {EXPERIMENT}",
"",
"状态体积之外的弃权回退(不训练):冻结的 V11 C1 在渲染不透明度 < 0.5 的 FAR 像素上弃权,改用调和插值补洞(AHFILL8)。主队列 EVAL-FAR 的 query 远离上下文帧,此前从未被任何模型读取。协议见 [PROTOCOL.md](PROTOCOL.md)。",
"",
f"- **P1 ABSTAIN_GAIN(HFILL8 − AHFILL8):** {fmt(primary['ABSTAIN_GAIN']['gain'])},{result['ABSTAIN_STATUS']}",
f"- **P2 调和插值补洞 − AHFILL8:** {fmt(primary['HARMONIC_VS_AHFILL8']['gain'])},{result['HARMONIC_LABEL']}",
f"- **分支 {result['INTERPRETATION_BRANCH']}**;复现 V11 封存预测的最大差 {worst}",
]
for cohort, block in result["cohorts"].items():
cov = block["coverage"]
lines += [
"",
f"## {cohort}({block['n_scenes']} 个场景)",
"",
f"GT 表面落在体积内 {cov['surface_in_volume']:.1%};FAR 像素占 {cov['far_fraction']:.1%},其中被标记 {cov['flagged_share_of_far']:.1%};标记对体积外像素的精度 {cov['precision_outside_volume']:.2f}、召回 {cov['recall_outside_volume']:.2f}。",
"",
"| 方法 | ALL | NEAR | FAR |",
"|---|---:|---:|---:|",
]
for method in METHODS:
cells = [block["absolute_absrel"].get(method, {}).get(region) for region in REGIONS]
lines.append(
f"| {method} | "
+ " | ".join("—" if not c else f"{c['mean']:.4f}" for c in cells)
+ " |"
)
lines += [
"",
"| 对比 | 定义 | 差值 [95% CI],场景 好/平/差 | 标签 | gate |",
"|---|---|---|---|---|",
]
for name, entry in block["contrasts"].items():
lines.append(
f"| {name} | {entry['definition']} | {fmt(entry['gain'])} | {entry.get('label', 'UNDEFINED')} | {entry.get('gate', 'UNDEFINED')} |"
)
lines += [
"",
"**限制:** EVAL-FAR 的场景已被用于近处 query,它是机制队列,不作资格验证;阈值 0.5 来自已用队列上的探索性分析;受保护的 final holdout 未打开。",
]
(root / "README.md").write_text("\n".join(lines) + "\n")
summary = [
"RGBD VOLUME V13",
f"ABSTAIN_STATUS={result['ABSTAIN_STATUS']}",
f"HARMONIC_LABEL={result['HARMONIC_LABEL']}",
f"INTERPRETATION_BRANCH={result['INTERPRETATION_BRANCH']}",
f"REPLAY_MAX_ABS_DEVIATION={worst}",
]
for cohort, block in result["cohorts"].items():
for method in ("CONSTANT", "REPROJ_HARMONIC", "C1", "HFILL8", "AHFILL8"):
value = block["absolute_absrel"].get(method, {}).get("ALL")
if value:
summary.append(f"{cohort}_{method}_ALL={value['mean']:.6f}")
for name in ("ABSTAIN_GAIN", "HARMONIC_VS_AHFILL8", "CONSTANT_VS_C1"):
entry = block["contrasts"][name]
if entry["gain"]:
summary.append(
f"{cohort}_{name}={entry['gain']['mean']:.6f} CI={entry['gain']['ci95']} LABEL={entry['label']} GATE={entry['gate']}"
)
summary.append(f"{cohort}_SURFACE_IN_VOLUME={block['coverage']['surface_in_volume']:.4f}")
summary.append("FINAL_HOLDOUT_TOUCHED=false")
(root / "terminal_summary.txt").write_text("\n".join(summary) + "\n")
print("\n".join(summary), flush=True)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--stage", choices=("lock", "seal", "score", "analyze"), required=True)
parser.add_argument("--cohort", choices=tuple(COHORTS))
parser.add_argument("--root", type=Path, default=Path("outputs") / EXPERIMENT)
args = parser.parse_args()
torch.set_num_threads(1)
if args.stage == "seal":
if args.cohort is None:
parser.error("--stage seal needs --cohort")
seal(args.root, args.cohort)
else:
{"lock": lock, "score": score, "analyze": analyze}[args.stage](args.root)
if __name__ == "__main__":
main()