support markdown-free CTT summary
Browse files- scripts/summarize_ctt_runs.py +65 -10
scripts/summarize_ctt_runs.py
CHANGED
|
@@ -27,6 +27,7 @@ COLUMNS = [
|
|
| 27 |
"pptc_0p20",
|
| 28 |
"pptc_0p40",
|
| 29 |
"negative_near_0p20",
|
|
|
|
| 30 |
"calibration_ece",
|
| 31 |
"seeds",
|
| 32 |
"ci",
|
|
@@ -38,9 +39,15 @@ def main(argv: list[str] | None = None) -> int:
|
|
| 38 |
parser.add_argument("--run-root", type=Path, default=Path("runs"))
|
| 39 |
parser.add_argument("--out-csv", type=Path, default=Path("runs/summary_ctt.csv"))
|
| 40 |
parser.add_argument("--out-md", type=Path, default=Path("runs/summary_ctt.md"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
args = parser.parse_args(argv)
|
| 42 |
|
| 43 |
rows = []
|
|
|
|
| 44 |
reproduce = args.run_root / "reproduce_v0" / "metrics.json"
|
| 45 |
if reproduce.exists():
|
| 46 |
rows.extend(_reproduce_rows(reproduce))
|
|
@@ -50,9 +57,11 @@ def main(argv: list[str] | None = None) -> int:
|
|
| 50 |
"ctt_val_proxy_comparison",
|
| 51 |
"dominance_calibrated_selector_eval",
|
| 52 |
"learned_dominance_selector_eval",
|
|
|
|
| 53 |
}:
|
| 54 |
continue
|
| 55 |
rows.append(_ctt_row(metrics_path))
|
|
|
|
| 56 |
for metrics_path in sorted(args.run_root.glob("*memory*_val_proxy/metrics.json")):
|
| 57 |
rows.append(_positive_memory_row(metrics_path))
|
| 58 |
local_atlas = args.run_root / "local_atlas_val_proxy" / "metrics.json"
|
|
@@ -64,12 +73,30 @@ def main(argv: list[str] | None = None) -> int:
|
|
| 64 |
utility = args.run_root / "utility_energy_smoke" / "metrics.json"
|
| 65 |
if utility.exists():
|
| 66 |
rows.append(_utility_row(utility))
|
| 67 |
-
for
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
for metrics_path in sorted(args.run_root.glob("ctt*_nonlinear_dominance*/metrics.json")):
|
|
|
|
|
|
|
| 72 |
rows.append(_dominance_row(metrics_path))
|
|
|
|
| 73 |
|
| 74 |
args.out_csv.parent.mkdir(parents=True, exist_ok=True)
|
| 75 |
with args.out_csv.open("w", newline="") as handle:
|
|
@@ -77,8 +104,14 @@ def main(argv: list[str] | None = None) -> int:
|
|
| 77 |
writer.writeheader()
|
| 78 |
for row in rows:
|
| 79 |
writer.writerow({column: row.get(column, "n/a") for column in COLUMNS})
|
| 80 |
-
args.
|
| 81 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
return 0
|
| 83 |
|
| 84 |
|
|
@@ -113,6 +146,7 @@ def _reproduce_rows(path: Path) -> list[dict[str, str]]:
|
|
| 113 |
"negative_near_0p20": _fmt(
|
| 114 |
proxy.get("negative_near_at_0p20", proxy.get("negative_near_at_16_thr_0p2"))
|
| 115 |
),
|
|
|
|
| 116 |
"calibration_ece": "n/a",
|
| 117 |
"seeds": "3",
|
| 118 |
"ci": "see run",
|
|
@@ -144,6 +178,7 @@ def _ctt_row(path: Path) -> dict[str, str]:
|
|
| 144 |
"pptc_0p20": _summary_mean(summary, "pptc_at_16_thr_0p20"),
|
| 145 |
"pptc_0p40": _summary_mean(summary, "pptc_at_16_thr_0p40"),
|
| 146 |
"negative_near_0p20": _summary_mean(summary, "negative_near_at_16_thr_0p20"),
|
|
|
|
| 147 |
"calibration_ece": "n/a",
|
| 148 |
"seeds": _seed_count(path.parent),
|
| 149 |
"ci": "see run",
|
|
@@ -172,6 +207,7 @@ def _positive_memory_row(path: Path) -> dict[str, str]:
|
|
| 172 |
"pptc_0p20": _summary_mean(summary, "pptc_at_16_thr_0p20"),
|
| 173 |
"pptc_0p40": _summary_mean(summary, "pptc_at_16_thr_0p40"),
|
| 174 |
"negative_near_0p20": _summary_mean(summary, "negative_near_at_16_thr_0p20"),
|
|
|
|
| 175 |
"calibration_ece": "n/a",
|
| 176 |
"seeds": _seed_count(path.parent),
|
| 177 |
"ci": "see run",
|
|
@@ -204,6 +240,7 @@ def _comparison_rows(path: Path) -> list[dict[str, str]]:
|
|
| 204 |
"pptc_0p20": _fmt(item.get("pptc_0p20")),
|
| 205 |
"pptc_0p40": _fmt(item.get("pptc_0p40")),
|
| 206 |
"negative_near_0p20": _fmt(item.get("negative_near_0p20")),
|
|
|
|
| 207 |
"calibration_ece": "n/a",
|
| 208 |
"seeds": str(item.get("train_seeds", "unknown")),
|
| 209 |
"ci": "aggregate over run means",
|
|
@@ -232,6 +269,7 @@ def _utility_row(path: Path) -> dict[str, str]:
|
|
| 232 |
"pptc_0p20": "n/a",
|
| 233 |
"pptc_0p40": "n/a",
|
| 234 |
"negative_near_0p20": "n/a",
|
|
|
|
| 235 |
"calibration_ece": _summary_mean(data.get("summary", {}), "pairwise_causal_calibration_ece"),
|
| 236 |
"seeds": "train split",
|
| 237 |
"ci": "see run",
|
|
@@ -248,6 +286,11 @@ def _dominance_row(path: Path) -> dict[str, str]:
|
|
| 248 |
proposal_oracle = _as_float(summary.get("proposal_oracle_success"))
|
| 249 |
success_support_gap = _as_float(summary.get("success_support_gap"))
|
| 250 |
success_selector_gap = _as_float(summary.get("success_selector_gap"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 251 |
seed_count = _dominance_seed_count(data)
|
| 252 |
target = data.get("target")
|
| 253 |
feature_set = data.get("feature_set")
|
|
@@ -285,12 +328,23 @@ def _dominance_row(path: Path) -> dict[str, str]:
|
|
| 285 |
"pptc_0p20": "n/a",
|
| 286 |
"pptc_0p40": "n/a",
|
| 287 |
"negative_near_0p20": "n/a",
|
| 288 |
-
"
|
|
|
|
| 289 |
"seeds": str(seed_count),
|
| 290 |
"ci": "see run",
|
| 291 |
}
|
| 292 |
|
| 293 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 294 |
def _dominance_method_name(path: Path, data: dict[str, Any]) -> str:
|
| 295 |
if "nonlinear" in path.parent.name:
|
| 296 |
target = data.get("target") or "positive_margin"
|
|
@@ -365,6 +419,7 @@ def _measured_ctt_row(path: Path, data: dict[str, Any]) -> dict[str, str]:
|
|
| 365 |
"pptc_0p20": "n/a",
|
| 366 |
"pptc_0p40": "n/a",
|
| 367 |
"negative_near_0p20": "n/a",
|
|
|
|
| 368 |
"calibration_ece": _fmt(ece),
|
| 369 |
"seeds": str(seed_count),
|
| 370 |
"ci": "see measured_metrics",
|
|
@@ -493,8 +548,8 @@ def _markdown(rows: list[dict[str, str]]) -> str:
|
|
| 493 |
lines = [
|
| 494 |
"# CTT Summary",
|
| 495 |
"",
|
| 496 |
-
"| Method | Status | Base | Selected | Proposal oracle | Hidden oracle | Utility support gap | Utility selector gap | Success support gap | Success selector gap | OutcomePTR | PPTC@0.20 | PPTC@0.40 | NegativeNear@0.20 | Calibration ECE | Run |",
|
| 497 |
-
"| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- |",
|
| 498 |
]
|
| 499 |
for row in rows:
|
| 500 |
lines.append(
|
|
@@ -502,7 +557,7 @@ def _markdown(rows: list[dict[str, str]]) -> str:
|
|
| 502 |
"{proposal_oracle} | {hidden_oracle} | {support_gap} | {selector_gap} | "
|
| 503 |
"{success_support_gap} | {success_selector_gap} | {outcome_ptr} | "
|
| 504 |
"{pptc_0p20} | {pptc_0p40} | "
|
| 505 |
-
"{negative_near_0p20} | {calibration_ece} | `{run_path}` |".format(**row)
|
| 506 |
)
|
| 507 |
lines.append("")
|
| 508 |
lines.append(
|
|
|
|
| 27 |
"pptc_0p20",
|
| 28 |
"pptc_0p40",
|
| 29 |
"negative_near_0p20",
|
| 30 |
+
"unsafe_execution",
|
| 31 |
"calibration_ece",
|
| 32 |
"seeds",
|
| 33 |
"ci",
|
|
|
|
| 39 |
parser.add_argument("--run-root", type=Path, default=Path("runs"))
|
| 40 |
parser.add_argument("--out-csv", type=Path, default=Path("runs/summary_ctt.csv"))
|
| 41 |
parser.add_argument("--out-md", type=Path, default=Path("runs/summary_ctt.md"))
|
| 42 |
+
parser.add_argument(
|
| 43 |
+
"--no-markdown-report",
|
| 44 |
+
action="store_true",
|
| 45 |
+
help="Do not write --out-md; the persistent prose summary lives in README.md.",
|
| 46 |
+
)
|
| 47 |
args = parser.parse_args(argv)
|
| 48 |
|
| 49 |
rows = []
|
| 50 |
+
seen_metric_paths: set[Path] = set()
|
| 51 |
reproduce = args.run_root / "reproduce_v0" / "metrics.json"
|
| 52 |
if reproduce.exists():
|
| 53 |
rows.extend(_reproduce_rows(reproduce))
|
|
|
|
| 57 |
"ctt_val_proxy_comparison",
|
| 58 |
"dominance_calibrated_selector_eval",
|
| 59 |
"learned_dominance_selector_eval",
|
| 60 |
+
"nonlinear_dominance_selector_eval",
|
| 61 |
}:
|
| 62 |
continue
|
| 63 |
rows.append(_ctt_row(metrics_path))
|
| 64 |
+
seen_metric_paths.add(metrics_path)
|
| 65 |
for metrics_path in sorted(args.run_root.glob("*memory*_val_proxy/metrics.json")):
|
| 66 |
rows.append(_positive_memory_row(metrics_path))
|
| 67 |
local_atlas = args.run_root / "local_atlas_val_proxy" / "metrics.json"
|
|
|
|
| 73 |
utility = args.run_root / "utility_energy_smoke" / "metrics.json"
|
| 74 |
if utility.exists():
|
| 75 |
rows.append(_utility_row(utility))
|
| 76 |
+
for pattern in ("ctt_dominance*/metrics.json", "ctt*_dominance*/metrics.json"):
|
| 77 |
+
for metrics_path in sorted(args.run_root.glob(pattern)):
|
| 78 |
+
if metrics_path in seen_metric_paths:
|
| 79 |
+
continue
|
| 80 |
+
report_type = _report_type(metrics_path)
|
| 81 |
+
if report_type not in {
|
| 82 |
+
"dominance_calibrated_selector_eval",
|
| 83 |
+
"learned_dominance_selector_eval",
|
| 84 |
+
"nonlinear_dominance_selector_eval",
|
| 85 |
+
}:
|
| 86 |
+
continue
|
| 87 |
+
rows.append(_dominance_row(metrics_path))
|
| 88 |
+
seen_metric_paths.add(metrics_path)
|
| 89 |
+
for pattern in ("ctt_learned_dominance*/metrics.json", "ctt*_learned_dominance*/metrics.json"):
|
| 90 |
+
for metrics_path in sorted(args.run_root.glob(pattern)):
|
| 91 |
+
if metrics_path in seen_metric_paths:
|
| 92 |
+
continue
|
| 93 |
+
rows.append(_dominance_row(metrics_path))
|
| 94 |
+
seen_metric_paths.add(metrics_path)
|
| 95 |
for metrics_path in sorted(args.run_root.glob("ctt*_nonlinear_dominance*/metrics.json")):
|
| 96 |
+
if metrics_path in seen_metric_paths:
|
| 97 |
+
continue
|
| 98 |
rows.append(_dominance_row(metrics_path))
|
| 99 |
+
seen_metric_paths.add(metrics_path)
|
| 100 |
|
| 101 |
args.out_csv.parent.mkdir(parents=True, exist_ok=True)
|
| 102 |
with args.out_csv.open("w", newline="") as handle:
|
|
|
|
| 104 |
writer.writeheader()
|
| 105 |
for row in rows:
|
| 106 |
writer.writerow({column: row.get(column, "n/a") for column in COLUMNS})
|
| 107 |
+
if args.no_markdown_report:
|
| 108 |
+
if args.out_md.exists():
|
| 109 |
+
args.out_md.unlink()
|
| 110 |
+
md_path: str | None = None
|
| 111 |
+
else:
|
| 112 |
+
args.out_md.write_text(_markdown(rows) + "\n")
|
| 113 |
+
md_path = str(args.out_md)
|
| 114 |
+
print(json.dumps({"csv": str(args.out_csv), "md": md_path, "rows": len(rows)}, indent=2))
|
| 115 |
return 0
|
| 116 |
|
| 117 |
|
|
|
|
| 146 |
"negative_near_0p20": _fmt(
|
| 147 |
proxy.get("negative_near_at_0p20", proxy.get("negative_near_at_16_thr_0p2"))
|
| 148 |
),
|
| 149 |
+
"unsafe_execution": "n/a",
|
| 150 |
"calibration_ece": "n/a",
|
| 151 |
"seeds": "3",
|
| 152 |
"ci": "see run",
|
|
|
|
| 178 |
"pptc_0p20": _summary_mean(summary, "pptc_at_16_thr_0p20"),
|
| 179 |
"pptc_0p40": _summary_mean(summary, "pptc_at_16_thr_0p40"),
|
| 180 |
"negative_near_0p20": _summary_mean(summary, "negative_near_at_16_thr_0p20"),
|
| 181 |
+
"unsafe_execution": "n/a",
|
| 182 |
"calibration_ece": "n/a",
|
| 183 |
"seeds": _seed_count(path.parent),
|
| 184 |
"ci": "see run",
|
|
|
|
| 207 |
"pptc_0p20": _summary_mean(summary, "pptc_at_16_thr_0p20"),
|
| 208 |
"pptc_0p40": _summary_mean(summary, "pptc_at_16_thr_0p40"),
|
| 209 |
"negative_near_0p20": _summary_mean(summary, "negative_near_at_16_thr_0p20"),
|
| 210 |
+
"unsafe_execution": "n/a",
|
| 211 |
"calibration_ece": "n/a",
|
| 212 |
"seeds": _seed_count(path.parent),
|
| 213 |
"ci": "see run",
|
|
|
|
| 240 |
"pptc_0p20": _fmt(item.get("pptc_0p20")),
|
| 241 |
"pptc_0p40": _fmt(item.get("pptc_0p40")),
|
| 242 |
"negative_near_0p20": _fmt(item.get("negative_near_0p20")),
|
| 243 |
+
"unsafe_execution": "n/a",
|
| 244 |
"calibration_ece": "n/a",
|
| 245 |
"seeds": str(item.get("train_seeds", "unknown")),
|
| 246 |
"ci": "aggregate over run means",
|
|
|
|
| 269 |
"pptc_0p20": "n/a",
|
| 270 |
"pptc_0p40": "n/a",
|
| 271 |
"negative_near_0p20": "n/a",
|
| 272 |
+
"unsafe_execution": "n/a",
|
| 273 |
"calibration_ece": _summary_mean(data.get("summary", {}), "pairwise_causal_calibration_ece"),
|
| 274 |
"seeds": "train split",
|
| 275 |
"ci": "see run",
|
|
|
|
| 286 |
proposal_oracle = _as_float(summary.get("proposal_oracle_success"))
|
| 287 |
success_support_gap = _as_float(summary.get("success_support_gap"))
|
| 288 |
success_selector_gap = _as_float(summary.get("success_selector_gap"))
|
| 289 |
+
ece = _first_float(
|
| 290 |
+
_dominance_pairwise_ece(data, "eval"),
|
| 291 |
+
summary.get("pairwise_causal_calibration_ece"),
|
| 292 |
+
_metric_mean(metric_summary, "pairwise_causal_calibration_ece"),
|
| 293 |
+
)
|
| 294 |
seed_count = _dominance_seed_count(data)
|
| 295 |
target = data.get("target")
|
| 296 |
feature_set = data.get("feature_set")
|
|
|
|
| 328 |
"pptc_0p20": "n/a",
|
| 329 |
"pptc_0p40": "n/a",
|
| 330 |
"negative_near_0p20": "n/a",
|
| 331 |
+
"unsafe_execution": _fmt(summary.get("unsafe_execution_known")),
|
| 332 |
+
"calibration_ece": _fmt(ece),
|
| 333 |
"seeds": str(seed_count),
|
| 334 |
"ci": "see run",
|
| 335 |
}
|
| 336 |
|
| 337 |
|
| 338 |
+
def _dominance_pairwise_ece(data: dict[str, Any], split: str) -> float | None:
|
| 339 |
+
payload = data.get("pairwise_causal_calibration", {})
|
| 340 |
+
if not isinstance(payload, dict):
|
| 341 |
+
return None
|
| 342 |
+
split_payload = payload.get(split, {})
|
| 343 |
+
if not isinstance(split_payload, dict):
|
| 344 |
+
return None
|
| 345 |
+
return _as_float(split_payload.get("ece"))
|
| 346 |
+
|
| 347 |
+
|
| 348 |
def _dominance_method_name(path: Path, data: dict[str, Any]) -> str:
|
| 349 |
if "nonlinear" in path.parent.name:
|
| 350 |
target = data.get("target") or "positive_margin"
|
|
|
|
| 419 |
"pptc_0p20": "n/a",
|
| 420 |
"pptc_0p40": "n/a",
|
| 421 |
"negative_near_0p20": "n/a",
|
| 422 |
+
"unsafe_execution": _summary_mean(summary, f"selected_unsafe_known_at_{k}"),
|
| 423 |
"calibration_ece": _fmt(ece),
|
| 424 |
"seeds": str(seed_count),
|
| 425 |
"ci": "see measured_metrics",
|
|
|
|
| 548 |
lines = [
|
| 549 |
"# CTT Summary",
|
| 550 |
"",
|
| 551 |
+
"| Method | Status | Base | Selected | Proposal oracle | Hidden oracle | Utility support gap | Utility selector gap | Success support gap | Success selector gap | OutcomePTR | PPTC@0.20 | PPTC@0.40 | NegativeNear@0.20 | Unsafe exec. | Calibration ECE | Run |",
|
| 552 |
+
"| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- |",
|
| 553 |
]
|
| 554 |
for row in rows:
|
| 555 |
lines.append(
|
|
|
|
| 557 |
"{proposal_oracle} | {hidden_oracle} | {support_gap} | {selector_gap} | "
|
| 558 |
"{success_support_gap} | {success_selector_gap} | {outcome_ptr} | "
|
| 559 |
"{pptc_0p20} | {pptc_0p40} | "
|
| 560 |
+
"{negative_near_0p20} | {unsafe_execution} | {calibration_ece} | `{run_path}` |".format(**row)
|
| 561 |
)
|
| 562 |
lines.append("")
|
| 563 |
lines.append(
|