auto-sync 2026-07-03T00:30:47Z workspace (part 3)
Browse files- workspace/runs/leakage_audit/report.json +4 -4
- workspace/runs/leakage_audit/report.md +2 -2
- workspace/runs/summary_ctt.csv +4 -0
- workspace/runs/summary_ctt.md +4 -0
- workspace/scripts/audit_cil_charts.py +5 -2
- workspace/scripts/eval_ctt_proxy.py +49 -8
- workspace/scripts/eval_metrics.py +11 -20
- workspace/scripts/summarize_ctt_runs.py +40 -1
workspace/runs/leakage_audit/report.json
CHANGED
|
@@ -3,8 +3,8 @@
|
|
| 3 |
"indexes": {
|
| 4 |
"test": {
|
| 5 |
"audience": "evaluator_only",
|
| 6 |
-
"content_hash": "
|
| 7 |
-
"include_outcomes":
|
| 8 |
"num_groups_exported": 410,
|
| 9 |
"num_rows": 6560,
|
| 10 |
"path": "data/cil_charts/test/index.json",
|
|
@@ -23,8 +23,8 @@
|
|
| 23 |
},
|
| 24 |
"val": {
|
| 25 |
"audience": "evaluator_only",
|
| 26 |
-
"content_hash": "
|
| 27 |
-
"include_outcomes":
|
| 28 |
"num_groups_exported": 419,
|
| 29 |
"num_rows": 6704,
|
| 30 |
"path": "data/cil_charts/val/index.json",
|
|
|
|
| 3 |
"indexes": {
|
| 4 |
"test": {
|
| 5 |
"audience": "evaluator_only",
|
| 6 |
+
"content_hash": "1710ede7c73d8f72479fd63dd9941a0e8bb55211d3ea0267963abc9e7a043b91",
|
| 7 |
+
"include_outcomes": true,
|
| 8 |
"num_groups_exported": 410,
|
| 9 |
"num_rows": 6560,
|
| 10 |
"path": "data/cil_charts/test/index.json",
|
|
|
|
| 23 |
},
|
| 24 |
"val": {
|
| 25 |
"audience": "evaluator_only",
|
| 26 |
+
"content_hash": "fa0fe0e3881ee70ce91492445871614d01b3a47fc7c576659c53ff17ddee4e42",
|
| 27 |
+
"include_outcomes": true,
|
| 28 |
"num_groups_exported": 419,
|
| 29 |
"num_rows": 6704,
|
| 30 |
"path": "data/cil_charts/val/index.json",
|
workspace/runs/leakage_audit/report.md
CHANGED
|
@@ -4,9 +4,9 @@ Status: `pass`
|
|
| 4 |
|
| 5 |
| Split | Rows | Charts | Outcomes in DB | Retrieval allowed | Audience |
|
| 6 |
| --- | ---: | ---: | --- | --- | --- |
|
| 7 |
-
| test | 6560 | 410 |
|
| 8 |
| train | 32704 | 2044 | True | True | train_retrieval |
|
| 9 |
-
| val | 6704 | 419 |
|
| 10 |
|
| 11 |
## Violations
|
| 12 |
|
|
|
|
| 4 |
|
| 5 |
| Split | Rows | Charts | Outcomes in DB | Retrieval allowed | Audience |
|
| 6 |
| --- | ---: | ---: | --- | --- | --- |
|
| 7 |
+
| test | 6560 | 410 | True | False | evaluator_only |
|
| 8 |
| train | 32704 | 2044 | True | True | train_retrieval |
|
| 9 |
+
| val | 6704 | 419 | True | False | evaluator_only |
|
| 10 |
|
| 11 |
## Violations
|
| 12 |
|
workspace/runs/summary_ctt.csv
CHANGED
|
@@ -2,6 +2,10 @@ method,run_path,clean,status,success,proposal_oracle,support_gap,selector_gap,ou
|
|
| 2 |
V0 residual transport diagnostic,runs/reproduce_v0,yes,measured diagnostic baseline,0.3890,0.4435,0.1264,0.0545,n/a,0.2366,0.5269,0.0533,n/a,3,see run
|
| 3 |
CTT gated residual,runs/ctt_gated_residual_smoke,yes,train,n/a,n/a,n/a,n/a,n/a,n/a,n/a,n/a,n/a,unknown,see run
|
| 4 |
CTT gated residual,runs/ctt_gated_residual_smoke_proxy,yes,proxy,n/a,n/a,n/a,n/a,n/a,0.5000,0.7500,0.0573,n/a,unknown,see run
|
|
|
|
| 5 |
CTT residual train,runs/ctt_residual_smoke,yes,train,n/a,n/a,n/a,n/a,n/a,n/a,n/a,n/a,n/a,unknown,see run
|
| 6 |
CTT residual proxy,runs/ctt_residual_smoke_proxy,yes,proxy,n/a,n/a,n/a,n/a,n/a,1.0000,1.0000,0.1010,n/a,unknown,see run
|
|
|
|
|
|
|
|
|
|
| 7 |
Utility energy smoke,runs/utility_energy_smoke,train-only,calibration diagnostic,n/a,n/a,n/a,n/a,n/a,n/a,n/a,n/a,0.1316,train split,see run
|
|
|
|
| 2 |
V0 residual transport diagnostic,runs/reproduce_v0,yes,measured diagnostic baseline,0.3890,0.4435,0.1264,0.0545,n/a,0.2366,0.5269,0.0533,n/a,3,see run
|
| 3 |
CTT gated residual,runs/ctt_gated_residual_smoke,yes,train,n/a,n/a,n/a,n/a,n/a,n/a,n/a,n/a,n/a,unknown,see run
|
| 4 |
CTT gated residual,runs/ctt_gated_residual_smoke_proxy,yes,proxy,n/a,n/a,n/a,n/a,n/a,0.5000,0.7500,0.0573,n/a,unknown,see run
|
| 5 |
+
CTT gated residual,runs/ctt_gated_residual_val_proxy,yes,proxy,n/a,n/a,n/a,n/a,n/a,0.1739,0.5362,0.0281,n/a,47,see run
|
| 6 |
CTT residual train,runs/ctt_residual_smoke,yes,train,n/a,n/a,n/a,n/a,n/a,n/a,n/a,n/a,n/a,unknown,see run
|
| 7 |
CTT residual proxy,runs/ctt_residual_smoke_proxy,yes,proxy,n/a,n/a,n/a,n/a,n/a,1.0000,1.0000,0.1010,n/a,unknown,see run
|
| 8 |
+
CTT residual proxy,runs/ctt_residual_val_proxy,yes,proxy,n/a,n/a,n/a,n/a,n/a,0.3478,0.7101,0.0397,n/a,47,see run
|
| 9 |
+
task memory,runs/task_memory_val_proxy,yes,proxy baseline,n/a,n/a,n/a,n/a,n/a,0.3188,0.5362,0.0175,n/a,47,see run
|
| 10 |
+
local atlas,runs/local_atlas_val_proxy,yes,proxy baseline,n/a,n/a,n/a,n/a,n/a,0.4058,0.6812,0.0368,n/a,47,see run
|
| 11 |
Utility energy smoke,runs/utility_energy_smoke,train-only,calibration diagnostic,n/a,n/a,n/a,n/a,n/a,n/a,n/a,n/a,0.1316,train split,see run
|
workspace/runs/summary_ctt.md
CHANGED
|
@@ -5,8 +5,12 @@
|
|
| 5 |
| V0 residual transport diagnostic | measured diagnostic baseline | 0.3890 | 0.4435 | 0.1264 | 0.0545 | n/a | 0.2366 | 0.5269 | 0.0533 | n/a | `runs/reproduce_v0` |
|
| 6 |
| CTT gated residual | train | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | `runs/ctt_gated_residual_smoke` |
|
| 7 |
| CTT gated residual | proxy | n/a | n/a | n/a | n/a | n/a | 0.5000 | 0.7500 | 0.0573 | n/a | `runs/ctt_gated_residual_smoke_proxy` |
|
|
|
|
| 8 |
| CTT residual train | train | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | `runs/ctt_residual_smoke` |
|
| 9 |
| CTT residual proxy | proxy | n/a | n/a | n/a | n/a | n/a | 1.0000 | 1.0000 | 0.1010 | n/a | `runs/ctt_residual_smoke_proxy` |
|
|
|
|
|
|
|
|
|
|
| 10 |
| Utility energy smoke | calibration diagnostic | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | 0.1316 | `runs/utility_energy_smoke` |
|
| 11 |
|
| 12 |
`n/a` means the required measured artifact does not exist yet.
|
|
|
|
| 5 |
| V0 residual transport diagnostic | measured diagnostic baseline | 0.3890 | 0.4435 | 0.1264 | 0.0545 | n/a | 0.2366 | 0.5269 | 0.0533 | n/a | `runs/reproduce_v0` |
|
| 6 |
| CTT gated residual | train | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | `runs/ctt_gated_residual_smoke` |
|
| 7 |
| CTT gated residual | proxy | n/a | n/a | n/a | n/a | n/a | 0.5000 | 0.7500 | 0.0573 | n/a | `runs/ctt_gated_residual_smoke_proxy` |
|
| 8 |
+
| CTT gated residual | proxy | n/a | n/a | n/a | n/a | n/a | 0.1739 | 0.5362 | 0.0281 | n/a | `runs/ctt_gated_residual_val_proxy` |
|
| 9 |
| CTT residual train | train | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | `runs/ctt_residual_smoke` |
|
| 10 |
| CTT residual proxy | proxy | n/a | n/a | n/a | n/a | n/a | 1.0000 | 1.0000 | 0.1010 | n/a | `runs/ctt_residual_smoke_proxy` |
|
| 11 |
+
| CTT residual proxy | proxy | n/a | n/a | n/a | n/a | n/a | 0.3478 | 0.7101 | 0.0397 | n/a | `runs/ctt_residual_val_proxy` |
|
| 12 |
+
| task memory | proxy baseline | n/a | n/a | n/a | n/a | n/a | 0.3188 | 0.5362 | 0.0175 | n/a | `runs/task_memory_val_proxy` |
|
| 13 |
+
| local atlas | proxy baseline | n/a | n/a | n/a | n/a | n/a | 0.4058 | 0.6812 | 0.0368 | n/a | `runs/local_atlas_val_proxy` |
|
| 14 |
| Utility energy smoke | calibration diagnostic | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | 0.1316 | `runs/utility_energy_smoke` |
|
| 15 |
|
| 16 |
`n/a` means the required measured artifact does not exist yet.
|
workspace/scripts/audit_cil_charts.py
CHANGED
|
@@ -79,10 +79,13 @@ def _audit_split_flags(indexes: dict[str, tuple[Path, dict[str, Any]]], violatio
|
|
| 79 |
else:
|
| 80 |
if retrieval:
|
| 81 |
violations.append(f"{path}: non-train split is retrieval_index_allowed")
|
| 82 |
-
if include_outcomes:
|
| 83 |
-
violations.append(f"{path}: non-train split exposes outcomes in chart DB")
|
| 84 |
if audience != "evaluator_only":
|
| 85 |
violations.append(f"{path}: non-train split audience must be evaluator_only")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
if split != str(payload.get("split")):
|
| 87 |
violations.append(f"{path}: index split field does not match directory")
|
| 88 |
if not payload.get("deployment_candidate_excludes_expert", False):
|
|
|
|
| 79 |
else:
|
| 80 |
if retrieval:
|
| 81 |
violations.append(f"{path}: non-train split is retrieval_index_allowed")
|
|
|
|
|
|
|
| 82 |
if audience != "evaluator_only":
|
| 83 |
violations.append(f"{path}: non-train split audience must be evaluator_only")
|
| 84 |
+
contract = payload.get("leakage_contract", {})
|
| 85 |
+
if not bool(contract.get("deployment_must_not_read_outcomes", False)):
|
| 86 |
+
violations.append(
|
| 87 |
+
f"{path}: non-train split must declare deployment_must_not_read_outcomes=true"
|
| 88 |
+
)
|
| 89 |
if split != str(payload.get("split")):
|
| 90 |
violations.append(f"{path}: index split field does not match directory")
|
| 91 |
if not payload.get("deployment_candidate_excludes_expert", False):
|
workspace/scripts/eval_ctt_proxy.py
CHANGED
|
@@ -16,7 +16,10 @@ if str(PROJECT_ROOT) not in sys.path:
|
|
| 16 |
import torch # noqa: E402
|
| 17 |
|
| 18 |
from cil.metrics import ( # noqa: E402
|
|
|
|
|
|
|
| 19 |
macro_micro_summary,
|
|
|
|
| 20 |
negative_near_at_threshold,
|
| 21 |
positives_closer_than_negatives,
|
| 22 |
proxy_positive_tangent_coverage_at_k,
|
|
@@ -51,6 +54,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
| 51 |
|
| 52 |
source_charts, source_index = load_charts(args.source_index, max_charts=None)
|
| 53 |
target_charts, target_index = load_charts(args.target_index, max_charts=args.max_target_charts)
|
|
|
|
| 54 |
rows = []
|
| 55 |
log_lines = [
|
| 56 |
f"source_charts={len(source_charts)} target_charts={len(target_charts)} k={args.k}",
|
|
@@ -85,7 +89,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
| 85 |
row: dict[str, Any] = {
|
| 86 |
"chart_id": target.chart_id,
|
| 87 |
"task_id": target.task_id,
|
| 88 |
-
"seed":
|
| 89 |
"num_proposals": len(proposals),
|
| 90 |
}
|
| 91 |
for threshold in thresholds:
|
|
@@ -104,8 +108,14 @@ def main(argv: list[str] | None = None) -> int:
|
|
| 104 |
)
|
| 105 |
distance = proxy_support_distance(proposals, positives, k=args.k)
|
| 106 |
row[f"proxy_support_distance_at_{args.k}"] = distance
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
closer = positives_closer_than_negatives(proposals, positives, negatives, k=args.k)
|
| 108 |
row[f"pos_closer_than_neg_at_{args.k}"] = closer
|
|
|
|
|
|
|
| 109 |
rows.append(row)
|
| 110 |
|
| 111 |
metric_names = sorted(
|
|
@@ -134,7 +144,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
| 134 |
}
|
| 135 |
(out_dir / "metrics.json").write_text(json.dumps(metrics, indent=2, sort_keys=True) + "\n")
|
| 136 |
(out_dir / "metrics_by_task.json").write_text(json.dumps(_by_task(rows, metric_names), indent=2, sort_keys=True) + "\n")
|
| 137 |
-
(out_dir / "metrics_by_seed.json").write_text("
|
| 138 |
(out_dir / "eval.log").write_text("\n".join(log_lines) + "\n")
|
| 139 |
(out_dir / "train.log").write_text("see checkpoint run\n")
|
| 140 |
(out_dir / "table.tex").write_text(_table(summary) + "\n")
|
|
@@ -143,6 +153,29 @@ def main(argv: list[str] | None = None) -> int:
|
|
| 143 |
return 0
|
| 144 |
|
| 145 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 146 |
def _write_run_provenance(
|
| 147 |
out_dir: Path,
|
| 148 |
args: argparse.Namespace,
|
|
@@ -164,16 +197,24 @@ def _run(command: list[str]) -> str:
|
|
| 164 |
|
| 165 |
|
| 166 |
def _by_task(rows: list[dict[str, Any]], metric_names: list[str]) -> dict[str, dict[str, float]]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 167 |
output: dict[str, dict[str, float]] = {}
|
| 168 |
for row in rows:
|
| 169 |
-
|
| 170 |
-
output.setdefault(
|
| 171 |
-
for
|
| 172 |
-
|
| 173 |
for metric in metric_names:
|
| 174 |
-
values = [float(row[metric]) for row in
|
| 175 |
if values:
|
| 176 |
-
output[
|
| 177 |
return output
|
| 178 |
|
| 179 |
|
|
|
|
| 16 |
import torch # noqa: E402
|
| 17 |
|
| 18 |
from cil.metrics import ( # noqa: E402
|
| 19 |
+
candidate_diversity,
|
| 20 |
+
collapse_rate,
|
| 21 |
macro_micro_summary,
|
| 22 |
+
mean_nearest_distance_to_set,
|
| 23 |
negative_near_at_threshold,
|
| 24 |
positives_closer_than_negatives,
|
| 25 |
proxy_positive_tangent_coverage_at_k,
|
|
|
|
| 54 |
|
| 55 |
source_charts, source_index = load_charts(args.source_index, max_charts=None)
|
| 56 |
target_charts, target_index = load_charts(args.target_index, max_charts=args.max_target_charts)
|
| 57 |
+
_validate_indexes(args.source_index, source_index, args.target_index, target_index)
|
| 58 |
rows = []
|
| 59 |
log_lines = [
|
| 60 |
f"source_charts={len(source_charts)} target_charts={len(target_charts)} k={args.k}",
|
|
|
|
| 89 |
row: dict[str, Any] = {
|
| 90 |
"chart_id": target.chart_id,
|
| 91 |
"task_id": target.task_id,
|
| 92 |
+
"seed": target.seed,
|
| 93 |
"num_proposals": len(proposals),
|
| 94 |
}
|
| 95 |
for threshold in thresholds:
|
|
|
|
| 108 |
)
|
| 109 |
distance = proxy_support_distance(proposals, positives, k=args.k)
|
| 110 |
row[f"proxy_support_distance_at_{args.k}"] = distance
|
| 111 |
+
positive_distance = mean_nearest_distance_to_set(proposals, positives, k=args.k)
|
| 112 |
+
row[f"mean_positive_distance_at_{args.k}"] = positive_distance
|
| 113 |
+
negative_distance = mean_nearest_distance_to_set(proposals, negatives, k=args.k)
|
| 114 |
+
row[f"mean_negative_distance_at_{args.k}"] = negative_distance
|
| 115 |
closer = positives_closer_than_negatives(proposals, positives, negatives, k=args.k)
|
| 116 |
row[f"pos_closer_than_neg_at_{args.k}"] = closer
|
| 117 |
+
row[f"candidate_diversity_at_{args.k}"] = candidate_diversity(proposals, k=args.k)
|
| 118 |
+
row[f"collapse_rate_at_{args.k}"] = collapse_rate(proposals, k=args.k)
|
| 119 |
rows.append(row)
|
| 120 |
|
| 121 |
metric_names = sorted(
|
|
|
|
| 144 |
}
|
| 145 |
(out_dir / "metrics.json").write_text(json.dumps(metrics, indent=2, sort_keys=True) + "\n")
|
| 146 |
(out_dir / "metrics_by_task.json").write_text(json.dumps(_by_task(rows, metric_names), indent=2, sort_keys=True) + "\n")
|
| 147 |
+
(out_dir / "metrics_by_seed.json").write_text(json.dumps(_by_group(rows, metric_names, "seed"), indent=2, sort_keys=True) + "\n")
|
| 148 |
(out_dir / "eval.log").write_text("\n".join(log_lines) + "\n")
|
| 149 |
(out_dir / "train.log").write_text("see checkpoint run\n")
|
| 150 |
(out_dir / "table.tex").write_text(_table(summary) + "\n")
|
|
|
|
| 153 |
return 0
|
| 154 |
|
| 155 |
|
| 156 |
+
def _validate_indexes(
|
| 157 |
+
source_path: Path,
|
| 158 |
+
source_index: dict[str, Any],
|
| 159 |
+
target_path: Path,
|
| 160 |
+
target_index: dict[str, Any],
|
| 161 |
+
) -> None:
|
| 162 |
+
if source_index.get("split") != "train" or not source_index.get("retrieval_index_allowed"):
|
| 163 |
+
raise SystemExit(
|
| 164 |
+
f"{source_path} is not a train-only retrieval index; CTT proxy eval must "
|
| 165 |
+
"retrieve source positives from train split only"
|
| 166 |
+
)
|
| 167 |
+
if not source_index.get("include_outcomes"):
|
| 168 |
+
raise SystemExit(f"{source_path} must include train outcomes for source positives")
|
| 169 |
+
if not target_index.get("include_outcomes"):
|
| 170 |
+
raise SystemExit(
|
| 171 |
+
f"{target_path} does not expose evaluator outcomes/labels. "
|
| 172 |
+
"Proxy support evaluation needs an evaluator-only target chart DB; "
|
| 173 |
+
"do not substitute hidden labels or distance proxies from train self-target."
|
| 174 |
+
)
|
| 175 |
+
if target_index.get("split") != "train" and target_index.get("retrieval_index_allowed"):
|
| 176 |
+
raise SystemExit(f"{target_path} is non-train but marked retrieval_index_allowed")
|
| 177 |
+
|
| 178 |
+
|
| 179 |
def _write_run_provenance(
|
| 180 |
out_dir: Path,
|
| 181 |
args: argparse.Namespace,
|
|
|
|
| 197 |
|
| 198 |
|
| 199 |
def _by_task(rows: list[dict[str, Any]], metric_names: list[str]) -> dict[str, dict[str, float]]:
|
| 200 |
+
return _by_group(rows, metric_names, "task_id")
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
def _by_group(
|
| 204 |
+
rows: list[dict[str, Any]],
|
| 205 |
+
metric_names: list[str],
|
| 206 |
+
group_key: str,
|
| 207 |
+
) -> dict[str, dict[str, float]]:
|
| 208 |
output: dict[str, dict[str, float]] = {}
|
| 209 |
for row in rows:
|
| 210 |
+
group = str(row[group_key])
|
| 211 |
+
output.setdefault(group, {})
|
| 212 |
+
for group in output:
|
| 213 |
+
group_rows = [row for row in rows if str(row[group_key]) == group]
|
| 214 |
for metric in metric_names:
|
| 215 |
+
values = [float(row[metric]) for row in group_rows if isinstance(row.get(metric), (int, float))]
|
| 216 |
if values:
|
| 217 |
+
output[group][metric] = sum(values) / len(values)
|
| 218 |
return output
|
| 219 |
|
| 220 |
|
workspace/scripts/eval_metrics.py
CHANGED
|
@@ -16,7 +16,10 @@ if str(PROJECT_ROOT) not in sys.path:
|
|
| 16 |
from cil.metrics import ( # noqa: E402
|
| 17 |
MetricInputError,
|
| 18 |
branch_car,
|
|
|
|
|
|
|
| 19 |
macro_micro_summary,
|
|
|
|
| 20 |
measured_support_gap,
|
| 21 |
negative_near_at_threshold,
|
| 22 |
outcome_ptr_at_k,
|
|
@@ -178,11 +181,17 @@ def _proxy_row(row: dict[str, Any], *, k: int, thresholds: list[float]) -> dict[
|
|
| 178 |
distance = proxy_support_distance(generated, positives, k=k)
|
| 179 |
if distance is not None:
|
| 180 |
output[f"proxy_support_distance_at_{k}"] = distance
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
closer = positives_closer_than_negatives(generated, positives, negatives, k=k)
|
| 182 |
if closer is not None:
|
| 183 |
output[f"pos_closer_than_neg_at_{k}"] = closer
|
| 184 |
-
output[f"candidate_diversity_at_{k}"] =
|
| 185 |
-
output[f"collapse_rate_at_{k}"] =
|
| 186 |
return output
|
| 187 |
|
| 188 |
|
|
@@ -297,24 +306,6 @@ def _markdown_report(mode: str, k: int, summary: dict[str, Any]) -> str:
|
|
| 297 |
return "\n".join(lines)
|
| 298 |
|
| 299 |
|
| 300 |
-
def _mean_pairwise_distance(vectors: list[list[float]]) -> float:
|
| 301 |
-
if len(vectors) < 2:
|
| 302 |
-
return 0.0
|
| 303 |
-
distances = []
|
| 304 |
-
for left_index, left in enumerate(vectors):
|
| 305 |
-
for right in vectors[left_index + 1 :]:
|
| 306 |
-
distances.append(_rms_l2(left, right))
|
| 307 |
-
return sum(distances) / len(distances)
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
def _collapse_rate(vectors: list[list[float]], *, threshold: float = 1.0e-6) -> float:
|
| 311 |
-
if not vectors:
|
| 312 |
-
return 0.0
|
| 313 |
-
first = vectors[0]
|
| 314 |
-
collapsed = sum(1 for vector in vectors if _rms_l2(first, vector) <= threshold)
|
| 315 |
-
return collapsed / len(vectors)
|
| 316 |
-
|
| 317 |
-
|
| 318 |
def _rms_l2(left: list[float], right: list[float]) -> float:
|
| 319 |
if len(left) != len(right):
|
| 320 |
raise MetricInputError("vectors must have matching dimensions")
|
|
|
|
| 16 |
from cil.metrics import ( # noqa: E402
|
| 17 |
MetricInputError,
|
| 18 |
branch_car,
|
| 19 |
+
candidate_diversity,
|
| 20 |
+
collapse_rate,
|
| 21 |
macro_micro_summary,
|
| 22 |
+
mean_nearest_distance_to_set,
|
| 23 |
measured_support_gap,
|
| 24 |
negative_near_at_threshold,
|
| 25 |
outcome_ptr_at_k,
|
|
|
|
| 181 |
distance = proxy_support_distance(generated, positives, k=k)
|
| 182 |
if distance is not None:
|
| 183 |
output[f"proxy_support_distance_at_{k}"] = distance
|
| 184 |
+
positive_distance = mean_nearest_distance_to_set(generated, positives, k=k)
|
| 185 |
+
if positive_distance is not None:
|
| 186 |
+
output[f"mean_positive_distance_at_{k}"] = positive_distance
|
| 187 |
+
negative_distance = mean_nearest_distance_to_set(generated, negatives, k=k)
|
| 188 |
+
if negative_distance is not None:
|
| 189 |
+
output[f"mean_negative_distance_at_{k}"] = negative_distance
|
| 190 |
closer = positives_closer_than_negatives(generated, positives, negatives, k=k)
|
| 191 |
if closer is not None:
|
| 192 |
output[f"pos_closer_than_neg_at_{k}"] = closer
|
| 193 |
+
output[f"candidate_diversity_at_{k}"] = candidate_diversity(generated, k=k)
|
| 194 |
+
output[f"collapse_rate_at_{k}"] = collapse_rate(generated, k=k)
|
| 195 |
return output
|
| 196 |
|
| 197 |
|
|
|
|
| 306 |
return "\n".join(lines)
|
| 307 |
|
| 308 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 309 |
def _rms_l2(left: list[float], right: list[float]) -> float:
|
| 310 |
if len(left) != len(right):
|
| 311 |
raise MetricInputError("vectors must have matching dimensions")
|
workspace/scripts/summarize_ctt_runs.py
CHANGED
|
@@ -41,6 +41,11 @@ def main(argv: list[str] | None = None) -> int:
|
|
| 41 |
rows.extend(_reproduce_rows(reproduce))
|
| 42 |
for metrics_path in sorted(args.run_root.glob("ctt*/metrics.json")):
|
| 43 |
rows.append(_ctt_row(metrics_path))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
utility = args.run_root / "utility_energy_smoke" / "metrics.json"
|
| 45 |
if utility.exists():
|
| 46 |
rows.append(_utility_row(utility))
|
|
@@ -107,7 +112,30 @@ def _ctt_row(path: Path) -> dict[str, str]:
|
|
| 107 |
"pptc_0p40": _summary_mean(summary, "pptc_at_16_thr_0p40"),
|
| 108 |
"negative_near_0p20": _summary_mean(summary, "negative_near_at_16_thr_0p20"),
|
| 109 |
"calibration_ece": "n/a",
|
| 110 |
-
"seeds":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
"ci": "see run",
|
| 112 |
}
|
| 113 |
|
|
@@ -152,6 +180,17 @@ def _fmt(value: Any) -> str:
|
|
| 152 |
return f"{float(value):.4f}"
|
| 153 |
|
| 154 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
def _markdown(rows: list[dict[str, str]]) -> str:
|
| 156 |
lines = [
|
| 157 |
"# CTT Summary",
|
|
|
|
| 41 |
rows.extend(_reproduce_rows(reproduce))
|
| 42 |
for metrics_path in sorted(args.run_root.glob("ctt*/metrics.json")):
|
| 43 |
rows.append(_ctt_row(metrics_path))
|
| 44 |
+
for metrics_path in sorted(args.run_root.glob("*memory*_val_proxy/metrics.json")):
|
| 45 |
+
rows.append(_positive_memory_row(metrics_path))
|
| 46 |
+
local_atlas = args.run_root / "local_atlas_val_proxy" / "metrics.json"
|
| 47 |
+
if local_atlas.exists():
|
| 48 |
+
rows.append(_positive_memory_row(local_atlas))
|
| 49 |
utility = args.run_root / "utility_energy_smoke" / "metrics.json"
|
| 50 |
if utility.exists():
|
| 51 |
rows.append(_utility_row(utility))
|
|
|
|
| 112 |
"pptc_0p40": _summary_mean(summary, "pptc_at_16_thr_0p40"),
|
| 113 |
"negative_near_0p20": _summary_mean(summary, "negative_near_at_16_thr_0p20"),
|
| 114 |
"calibration_ece": "n/a",
|
| 115 |
+
"seeds": _seed_count(path.parent),
|
| 116 |
+
"ci": "see run",
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
def _positive_memory_row(path: Path) -> dict[str, str]:
|
| 121 |
+
data = json.loads(path.read_text())
|
| 122 |
+
summary = data.get("summary", {})
|
| 123 |
+
method = str(data.get("method", path.parent.name)).replace("_", " ")
|
| 124 |
+
return {
|
| 125 |
+
"method": method,
|
| 126 |
+
"run_path": str(path.parent),
|
| 127 |
+
"clean": "yes",
|
| 128 |
+
"status": "proxy baseline",
|
| 129 |
+
"success": "n/a",
|
| 130 |
+
"proposal_oracle": "n/a",
|
| 131 |
+
"support_gap": "n/a",
|
| 132 |
+
"selector_gap": "n/a",
|
| 133 |
+
"outcome_ptr": "n/a",
|
| 134 |
+
"pptc_0p20": _summary_mean(summary, "pptc_at_16_thr_0p20"),
|
| 135 |
+
"pptc_0p40": _summary_mean(summary, "pptc_at_16_thr_0p40"),
|
| 136 |
+
"negative_near_0p20": _summary_mean(summary, "negative_near_at_16_thr_0p20"),
|
| 137 |
+
"calibration_ece": "n/a",
|
| 138 |
+
"seeds": _seed_count(path.parent),
|
| 139 |
"ci": "see run",
|
| 140 |
}
|
| 141 |
|
|
|
|
| 180 |
return f"{float(value):.4f}"
|
| 181 |
|
| 182 |
|
| 183 |
+
def _seed_count(run_dir: Path) -> str:
|
| 184 |
+
path = run_dir / "metrics_by_seed.json"
|
| 185 |
+
if not path.exists():
|
| 186 |
+
return "unknown"
|
| 187 |
+
try:
|
| 188 |
+
data = json.loads(path.read_text())
|
| 189 |
+
except json.JSONDecodeError:
|
| 190 |
+
return "unknown"
|
| 191 |
+
return str(len(data)) if isinstance(data, dict) and data else "unknown"
|
| 192 |
+
|
| 193 |
+
|
| 194 |
def _markdown(rows: list[dict[str, str]]) -> str:
|
| 195 |
lines = [
|
| 196 |
"# CTT Summary",
|