Skip to content

Commit bf0d905

Browse files
andre15silvaclaude
andcommitted
feat(paper): overhaul figures pipeline — PDF output, hparam table, Pro results, lookahead variants
- Switch all figure exports from PNG to PDF; update dashboard to render PDFs via <embed> - Merge run_figures.py + src/figures.py into run_paper_figures.py; delete old files - Add paper/style.py: centralised visual style (colors, hatch, rcParams, figure dimensions) - Add paper/__init__.py to make paper/ a package - Add build_hparam_table() reading from paper/hparams.json; populate Verified values from confirmed SLURM training logs, Laguna Pro per-probe HPs from independent sweeps - Extend build_auc_table() and build_calibration_table() with SWE-bench Pro sections - Add plot_generalization_barplot(): main 2×2 model×dataset AUC barplot - Add lookahead variants (max15, max50, _after_edit) via --lookahead-variants flag - Bold best layer per row in AUC table (was: global best per probe across all conditions) - Add colored deltas to transfer table - Update dashboard: load figures from paper/figures/, PDF embed support - Update slurm/figures.sh and README Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent a5b7d62 commit bf0d905

14 files changed

Lines changed: 1089 additions & 654 deletions

README.md

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Measures whether a language model's internal hidden states linearly predict properties of its own output before those properties are realised.
44

5-
Pipeline: inference + activation hooks → per-sample `.pt` files → per-(layer, probe) cache tensors → linear probe training with W&B sweep → accuracy heatmaps → static dashboard.
5+
Pipeline: inference + activation hooks → per-sample `.pt` files → per-(layer, probe) cache tensors → linear probe training with W&B sweep → paper figures → static dashboard.
66

77
## Pipeline overview
88

@@ -27,7 +27,8 @@ run_probe.py final train linear probe on best hparams → W&B run with met
2727
(or pass --from-sweep to auto-load best HPs from a sweep)
2828
2929
30-
run_figures.py accuracy heatmaps per layer × relative-position bin
30+
run_paper_figures.py AUC heatmaps, layer plots, lookahead, barplot, LaTeX tables
31+
→ paper/figures/ + paper/*.tex
3132
run_export_dashboard.py → dashboard/index.html (open in browser)
3233
```
3334

@@ -62,7 +63,7 @@ run_attach_labels_swebench.py load activations + trajectory + _labels.json,
6263
→ outputs/swebench/<run_id>_labeled/<instance_id>.pt
6364
6465
65-
run_build_cache.py → run_probe.py → run_figures.py (same as standard path)
66+
run_build_cache.py → run_probe.py → run_paper_figures.py (same as standard path)
6667
```
6768

6869
Dynamic probes (`currently_correct`, `currently_compiles`, `currently_reduces_failing`, `currently_has_regressions`) only make sense in the agentic path because they require `EditEvent` history with per-edit `test_results`. The carry-forward step in `run_attach_labels_swebench.py` expands one label per edit into one label per stride step; steps before the first edit are masked (`None``-1` → excluded from training).
@@ -258,10 +259,24 @@ Queries `sweep.best_run()` via the W&B API to auto-populate `lr`, `weight_decay`
258259
### 4. Figures
259260

260261
```bash
261-
uv run python run_figures.py \
262-
--run-id my_run \
263-
--probe will_be_correct \
264-
--model-config configs/models/qwen3_8b.yaml
262+
uv run python run_paper_figures.py \
263+
--results-dir results/swebench \
264+
--probes currently_compiles currently_correct currently_reduces_failing currently_has_regressions \
265+
--model-run-ids laguna_xs2_full qwen36_35b_a3b_full \
266+
--shuffled-run-ids laguna_xs2_full_shuffled qwen36_35b_a3b_full_shuffled
267+
```
268+
269+
This generates all publication figures under `paper/figures/` and LaTeX tables under `paper/`.
270+
It also writes `paper/figures/manifest.json` for the dashboard figures gallery.
271+
272+
For lookahead horizon figures, pass the shifted-label run IDs:
273+
274+
```bash
275+
uv run python run_paper_figures.py \
276+
... \
277+
--lookahead-run-ids laguna_xs2_full_shift0_max50 laguna_xs2_full_shift1_max50 ... laguna_xs2_full_shift50_max50 \
278+
--lookahead-k-values $(seq 0 50) \
279+
--lookahead-filename-suffix max50
265280
```
266281

267282
### 5. Export dashboard
@@ -311,8 +326,11 @@ sbatch slurm/probe_final.sh \
311326
--model-config configs/models/qwen3_8b.yaml \
312327
--from-sweep abc123xyz
313328

314-
sbatch slurm/figures.sh --run-id my_run --probe will_be_correct \
315-
--model-config configs/models/qwen3_8b.yaml
329+
sbatch slurm/figures.sh \
330+
--results-dir results/swebench \
331+
--probes currently_compiles currently_correct currently_reduces_failing currently_has_regressions \
332+
--model-run-ids laguna_xs2_full qwen36_35b_a3b_full \
333+
--shuffled-run-ids laguna_xs2_full_shuffled qwen36_35b_a3b_full_shuffled
316334

317335
sbatch slurm/export_dashboard.sh \
318336
--run-id my_run --probe will_be_correct \

dashboard/app.js

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use strict";
22

33
const DATA_ROOT = "data";
4+
const FIGURES_ROOT = "../paper/figures";
45
let state = {
56
manifest: [],
67
runId: null,
@@ -807,7 +808,7 @@ function switchTab(name) {
807808

808809
async function loadFiguresManifest() {
809810
try {
810-
return await fetchJSON(`${DATA_ROOT}/figures/manifest.json`);
811+
return await fetchJSON(`${FIGURES_ROOT}/manifest.json`);
811812
} catch {
812813
return [];
813814
}
@@ -824,7 +825,7 @@ function renderGallery() {
824825
);
825826

826827
if (filtered.length === 0) {
827-
container.innerHTML = '<p style="color:#888;font-size:13px;">No figures found. Run run_figures.py and re-export the dashboard.</p>';
828+
container.innerHTML = '<p style="color:#888;font-size:13px;">No figures found. Run run_paper_figures.py to generate figures and manifest.</p>';
828829
return;
829830
}
830831

@@ -853,19 +854,21 @@ function renderGallery() {
853854
card.style.cssText = "cursor:pointer;border:1px solid #ddd;border-radius:6px;overflow:hidden;width:200px;background:#fafafa;";
854855
card.title = fig.title || fig.path;
855856

856-
const img = document.createElement("img");
857857
const cacheBust = `?v=${Date.now()}`;
858-
img.src = `${DATA_ROOT}/figures/${fig.path}${cacheBust}`;
859-
img.style.cssText = "width:200px;height:130px;object-fit:contain;background:#fff;";
860-
img.loading = "lazy";
858+
const figSrc = `${FIGURES_ROOT}/${fig.path}${cacheBust}`;
859+
const embed = document.createElement("embed");
860+
embed.src = figSrc;
861+
embed.type = "application/pdf";
862+
embed.style.cssText = "width:200px;height:130px;background:#fff;pointer-events:none;";
861863

862864
const caption = document.createElement("div");
863-
caption.textContent = fig.title || fig.path.split("/").pop().replace(".png", "");
865+
const ext = fig.path.split(".").pop();
866+
caption.textContent = fig.title || fig.path.split("/").pop().replace(`.${ext}`, "");
864867
caption.style.cssText = "font-size:10px;color:#555;padding:4px 6px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;";
865868

866-
card.appendChild(img);
869+
card.appendChild(embed);
867870
card.appendChild(caption);
868-
card.addEventListener("click", () => openLightbox(`${DATA_ROOT}/figures/${fig.path}${cacheBust}`, fig.title || fig.path));
871+
card.addEventListener("click", () => openLightbox(figSrc, fig.title || fig.path));
869872
grid.appendChild(card);
870873
}
871874

dashboard/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ <h2>Detail View</h2>
7272

7373
<!-- Lightbox overlay -->
7474
<div id="lightbox" style="display:none; position:fixed; inset:0; background:rgba(0,0,0,0.85); z-index:1000; align-items:center; justify-content:center; flex-direction:column;">
75-
<img id="lightbox-img" style="max-width:92vw; max-height:88vh; border-radius:4px; box-shadow:0 4px 24px rgba(0,0,0,0.5);">
75+
<embed id="lightbox-img" type="application/pdf" style="max-width:92vw; width:900px; height:88vh; border-radius:4px; box-shadow:0 4px 24px rgba(0,0,0,0.5);">
7676
<div id="lightbox-caption" style="color:#eee; font-size:12px; margin-top:10px;"></div>
7777
<button onclick="closeLightbox()" style="position:absolute;top:16px;right:22px;font-size:24px;background:none;border:none;color:#fff;cursor:pointer;"></button>
7878
</div>

paper/__init__.py

Whitespace-only changes.

paper/auc_table.tex

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
\begin{table*}[h]
2+
\centering
3+
\caption{Test AUC-ROC per probe, model, and benchmark across transformer layers. \textbf{Bold} marks the best layer per row. \emph{Shuffled} uses label-permuted data as a sanity baseline.}
4+
\label{tab:auc_roc}
5+
\begin{tabular}{lccccc}
6+
\toprule
7+
& \multicolumn{4}{c}{AUC-ROC $\uparrow$} & Shuffled \\
8+
\cmidrule(lr){2-5}
9+
Probe & 10 & 20 & 30 & 39 & (best layer) \\
10+
\midrule
11+
\multicolumn{6}{l}{\textsc{SWE-bench Verified}} \\
12+
\addlinespace[2pt]
13+
\multicolumn{6}{l}{\quad\textit{Laguna-XS2}} \\
14+
Syntactic correctness & \textbf{0.698} & 0.669 & 0.679 & 0.626 & 0.502 \\
15+
Semantic correctness & 0.775 & \textbf{0.791} & 0.769 & 0.716 & 0.501 \\
16+
Reduces failures & 0.743 & \textbf{0.769} & 0.743 & 0.674 & 0.501 \\
17+
Has regressions & 0.688 & \textbf{0.697} & 0.675 & 0.612 & 0.501 \\
18+
\addlinespace[3pt]
19+
\multicolumn{6}{l}{\quad\textit{Qwen3.6-35B-A3B}} \\
20+
Syntactic correctness & \textbf{0.777} & 0.755 & 0.771 & 0.692 & 0.499 \\
21+
Semantic correctness & 0.816 & 0.836 & \textbf{0.839} & 0.786 & 0.503 \\
22+
Reduces failures & 0.818 & \textbf{0.835} & 0.832 & 0.776 & 0.506 \\
23+
Has regressions & 0.723 & 0.773 & \textbf{0.778} & 0.698 & 0.500 \\
24+
\midrule
25+
\multicolumn{6}{l}{\textsc{SWE-bench Pro}} \\
26+
\addlinespace[2pt]
27+
\multicolumn{6}{l}{\quad\textit{Laguna-XS2}} \\
28+
Syntactic correctness & \textbf{0.780} & 0.746 & 0.757 & 0.756 & — \\
29+
Semantic correctness & 0.681 & \textbf{0.726} & 0.714 & 0.670 & — \\
30+
Reduces failures & 0.681 & \textbf{0.718} & 0.697 & 0.657 & — \\
31+
Has regressions & 0.645 & 0.705 & \textbf{0.739} & 0.691 & — \\
32+
\addlinespace[3pt]
33+
\multicolumn{6}{l}{\quad\textit{Qwen3.6-35B-A3B}} \\
34+
Syntactic correctness & — & — & — & — & — \\
35+
Semantic correctness & — & — & — & — & — \\
36+
Reduces failures & — & — & — & — & — \\
37+
Has regressions & — & — & — & — & — \\
38+
\bottomrule
39+
\end{tabular}
40+
\end{table*}

paper/calibration_table.tex

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
\begin{table*}[h]
2+
\centering
3+
\caption{Calibration metrics (ECE and Brier score) per probe across layers. Lower is better for both metrics.}
4+
\label{tab:calibration}
5+
\begin{tabular}{lcccccccc}
6+
\toprule
7+
Probe & \multicolumn{2}{c}{Layer 10} & \multicolumn{2}{c}{Layer 20} & \multicolumn{2}{c}{Layer 30} & \multicolumn{2}{c}{Layer 39} \\
8+
\cmidrule(lr){2-9}
9+
& ECE $\downarrow$ & Brier $\downarrow$ & ECE $\downarrow$ & Brier $\downarrow$ & ECE $\downarrow$ & Brier $\downarrow$ & ECE $\downarrow$ & Brier $\downarrow$ \\
10+
\midrule
11+
\multicolumn{9}{l}{\textsc{SWE-bench Verified}} \\
12+
\addlinespace[2pt]
13+
\multicolumn{9}{l}{\quad\textit{Laguna-XS2}} \\
14+
Syntactic correctness & 0.043 & 0.089 & 0.045 & 0.090 & 0.048 & 0.089 & 0.060 & 0.093 \\
15+
Semantic correctness & 0.062 & 0.186 & 0.040 & 0.178 & 0.039 & 0.185 & 0.051 & 0.203 \\
16+
Reduces failures & 0.058 & 0.203 & 0.060 & 0.194 & 0.050 & 0.202 & 0.072 & 0.226 \\
17+
Has regressions & 0.029 & 0.096 & 0.035 & 0.096 & 0.044 & 0.098 & 0.056 & 0.103 \\
18+
\addlinespace[3pt]
19+
\multicolumn{9}{l}{\quad\textit{Qwen3.6-35B-A3B}} \\
20+
Syntactic correctness & 0.042 & 0.076 & 0.042 & 0.076 & 0.041 & 0.075 & 0.036 & 0.077 \\
21+
Semantic correctness & 0.050 & 0.152 & 0.035 & 0.144 & 0.035 & 0.142 & 0.070 & 0.166 \\
22+
Reduces failures & 0.062 & 0.161 & 0.050 & 0.153 & 0.043 & 0.153 & 0.072 & 0.178 \\
23+
Has regressions & 0.010 & 0.060 & 0.012 & 0.059 & 0.013 & 0.059 & 0.030 & 0.064 \\
24+
\midrule
25+
\multicolumn{9}{l}{\textsc{SWE-bench Pro}} \\
26+
\addlinespace[2pt]
27+
\multicolumn{9}{l}{\quad\textit{Laguna-XS2}} \\
28+
Syntactic correctness & 0.061 & 0.189 & 0.041 & 0.198 & 0.056 & 0.196 & 0.070 & 0.200 \\
29+
Semantic correctness & 0.029 & 0.090 & 0.025 & 0.087 & 0.027 & 0.088 & 0.045 & 0.091 \\
30+
Reduces failures & 0.048 & 0.138 & 0.045 & 0.133 & 0.054 & 0.136 & 0.069 & 0.142 \\
31+
Has regressions & 0.013 & 0.079 & 0.011 & 0.077 & 0.018 & 0.076 & 0.008 & 0.076 \\
32+
\addlinespace[3pt]
33+
\multicolumn{9}{l}{\quad\textit{Qwen3.6-35B-A3B}} \\
34+
Syntactic correctness & — & — & — & — & — & — & — & — \\
35+
Semantic correctness & — & — & — & — & — & — & — & — \\
36+
Reduces failures & — & — & — & — & — & — & — & — \\
37+
Has regressions & — & — & — & — & — & — & — & — \\
38+
\bottomrule
39+
\end{tabular}
40+
\end{table*}

paper/hparam_table.tex

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
\begin{table}[h]
2+
\centering
3+
\caption{Chosen hyperparameters for each model and benchmark, selected by 20-trial random search maximising mean validation AUC on the middle transformer layer. Pro sweeps were run independently per probe.}
4+
\label{tab:probe-hparams}
5+
\begin{tabular}{lllrrrr}
6+
\toprule
7+
Dataset & Model & Probe & Learning rate & Weight decay & Batch size & Patience \\
8+
\midrule
9+
\multirow{2}{*}{\textsc{Verified}} & Laguna-XS2 & & $3.42 \times 10^{-4}$ & $1.47 \times 10^{-3}$ & 256 & 50 \\
10+
& Qwen3.6-35B-A3B & & $1.17 \times 10^{-3}$ & $9.81 \times 10^{-2}$ & 1024 & 10 \\
11+
\midrule
12+
\multirow{5}{*}{\textsc{Pro}} & \multirow{4}{*}{Laguna-XS2} & Syntactic correctness & $2.53 \times 10^{-4}$ & $2.75 \times 10^{-4}$ & 256 & 50 \\
13+
& & Semantic correctness & $6.16 \times 10^{-4}$ & $5.18 \times 10^{-3}$ & 1024 & 25 \\
14+
& & Reduces failures & $2.31 \times 10^{-4}$ & $4.04 \times 10^{-3}$ & 256 & 10 \\
15+
& & Has regressions & $1.51 \times 10^{-4}$ & $3.26 \times 10^{-2}$ & 1024 & 10 \\
16+
& Qwen3.6-35B-A3B & & — & — & — & — \\
17+
\bottomrule
18+
\end{tabular}
19+
\end{table}

paper/hparams.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"Verified": {
3+
"Laguna-XS2": {"lr": 0.00034219718825575905, "weight_decay": 0.0014714964923484015, "batch_size": 256, "patience": 50},
4+
"Qwen3.6-35B-A3B": {"lr": 0.00117, "weight_decay": 0.0981, "batch_size": 1024, "patience": 10}
5+
},
6+
"Pro": {
7+
"Laguna-XS2": {
8+
"Syntactic correctness": {"lr": 0.0002528899667354841, "weight_decay": 0.00027521054719050283, "batch_size": 256, "patience": 50},
9+
"Semantic correctness": {"lr": 0.0006160451703789833, "weight_decay": 0.00517666984834032, "batch_size": 1024, "patience": 25},
10+
"Reduces failures": {"lr": 0.0002310173345942377, "weight_decay": 0.0040400920896719915, "batch_size": 256, "patience": 10},
11+
"Has regressions": {"lr": 0.00015055365890504895, "weight_decay": 0.0326146475479764, "batch_size": 1024, "patience": 10}
12+
},
13+
"Qwen3.6-35B-A3B": null
14+
}
15+
}

paper/style.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""Visual style constants for paper figures.
2+
3+
Adjust FULL_WIDTH for the target venue:
4+
NeurIPS : 5.50 in
5+
ICML : 6.75 in
6+
ICLR : 6.00 in (approx)
7+
"""
8+
9+
import matplotlib.pyplot as plt
10+
11+
# --- Figure dimensions ---
12+
FULL_WIDTH = 6.75 # inches; change per venue
13+
HALF_WIDTH = 3.3
14+
FIG_HEIGHT_BAR = 3.0
15+
FIG_HEIGHT_LINE = 2.6
16+
FIG_HEIGHT_HEAT = 2.8
17+
18+
# --- Color palette ---
19+
# Model identity = color family; dataset identity = shade + hatch
20+
# solid fill = Verified, hatched (///) = Pro
21+
COLORS = {
22+
"laguna_verified": "#1565C0", # dark blue
23+
"laguna_pro": "#64B5F6", # light blue
24+
"qwen_verified": "#BF360C", # dark coral
25+
"qwen_pro": "#FF8A65", # light coral
26+
"shuffled": "#9E9E9E", # neutral gray (both models)
27+
"baseline": "#AAAAAA", # random-baseline dashed line
28+
}
29+
30+
HATCH = {
31+
"verified": "", # solid fill
32+
"pro": "///", # diagonal hatch
33+
}
34+
35+
# --- Heatmap ---
36+
HEATMAP_CMAP = "Blues"
37+
HEATMAP_VMIN = 0.55
38+
HEATMAP_VMAX = 0.85
39+
40+
41+
def apply() -> None:
42+
"""Apply paper-wide rcParams. Call once at the top of main()."""
43+
plt.rcParams.update({
44+
"font.family": "sans-serif",
45+
"font.sans-serif": ["Helvetica", "Arial", "DejaVu Sans"],
46+
"font.size": 9,
47+
"axes.titlesize": 11,
48+
"axes.titleweight": "normal",
49+
"axes.labelsize": 10,
50+
"xtick.labelsize": 9,
51+
"ytick.labelsize": 9,
52+
"legend.fontsize": 8,
53+
"legend.framealpha": 0.9,
54+
"figure.dpi": 150,
55+
"savefig.dpi": 300,
56+
"savefig.bbox": "tight",
57+
"axes.spines.top": False,
58+
"axes.spines.right": False,
59+
"axes.grid": True,
60+
"grid.color": "#E0E0E0",
61+
"grid.linewidth": 0.6,
62+
"axes.axisbelow": True,
63+
})

0 commit comments

Comments
 (0)