@@ -1057,12 +1057,17 @@ def plot_layer_auc_grid(
10571057 )
10581058 axes_flat = axes .flatten ()
10591059
1060+ # Per-dataset-series markers: encode the legend entries (the data series),
1061+ # NOT the program property. Chosen distinct from the property markers
1062+ # ({^, o, s, D} used for the probe names) so the two encodings don't clash.
1063+ SERIES_MARKERS = ["P" , "X" , "*" , "h" ] # filled plus, filled x, star, hexagon
1064+
10601065 legend_handles : list = []
10611066 legend_built = False
10621067
1063- for ax , probe in zip (axes_flat , probes ):
1064- prop_key = PROBE_TO_PROPERTY . get ( probe )
1065- prop_marker = _style . PROPERTY_MARKERS [ prop_key ] if prop_key else "o"
1068+ for idx , ( ax , probe ) in enumerate ( zip (axes_flat , probes ) ):
1069+ row , col = idx // 2 , idx % 2
1070+ series_i = 0
10661071
10671072 for run_id , label in zip (run_ids , run_labels ):
10681073 all_res = _load (results_dir , run_id , probe )
@@ -1074,7 +1079,9 @@ def plot_layer_auc_grid(
10741079
10751080 is_qwen = "qwen" in run_id
10761081 color = _style .COLORS ["qwen_verified" if is_qwen else "laguna_verified" ]
1077- line , = ax .plot (xs , ys , linestyle = "-" , marker = prop_marker , markersize = 4 ,
1082+ marker = SERIES_MARKERS [series_i % len (SERIES_MARKERS )]
1083+ series_i += 1
1084+ line , = ax .plot (xs , ys , linestyle = "-" , marker = marker , markersize = 5 ,
10781085 color = color , linewidth = 1.4 , label = label )
10791086 if not legend_built :
10801087 legend_handles .append (line )
@@ -1086,14 +1093,16 @@ def plot_layer_auc_grid(
10861093 layer_aucs = _layer_means (all_res , "test_auc" , layers )
10871094 xs = [l for l in layers if not math .isnan (layer_aucs .get (l , float ("nan" )))]
10881095 ys = [layer_aucs [l ] for l in xs ]
1089- color = _style .COLORS ["qwen_pro" if "qwen" in extra_run_id else "laguna_pro" ]
1090- line , = ax .plot (xs , ys , linestyle = "--" , marker = prop_marker , markersize = 4 ,
1096+ color = _style .COLORS ["qwen_pro" if "qwen" in extra_run_id else "laguna_pro" ]
1097+ marker = SERIES_MARKERS [series_i % len (SERIES_MARKERS )]
1098+ series_i += 1
1099+ line , = ax .plot (xs , ys , linestyle = "--" , marker = marker , markersize = 5 ,
10911100 color = color , alpha = 0.85 , linewidth = 1.4 , label = extra_label )
10921101 if not legend_built :
10931102 legend_handles .append (line )
10941103
1095- rand_line = ax .axhline (0.5 , linestyle = ":" , color = _style . COLORS [ "baseline" ] ,
1096- linewidth = 1.0 , label = "Random" )
1104+ rand_line = ax .axhline (0.5 , linestyle = ( 0 , ( 1 , 1.5 )), color = "#C2C2C2" ,
1105+ linewidth = 1.4 , zorder = 0.5 , label = "Random" )
10971106 if not legend_built :
10981107 legend_handles .append (rand_line )
10991108 legend_built = True
@@ -1102,14 +1111,28 @@ def plot_layer_auc_grid(
11021111 ax .set_xticks (layers )
11031112 ax .set_xticklabels ([str (_layer_label (l )) for l in layers ])
11041113 ax .set_ylim (bottom = 0.45 , top = 1.0 )
1105- ax .set_ylabel ("AUC" )
11061114
1107- # n_test annotation — top-right of each panel
1115+ # Keep tick numbers readable on each panel without crossing the grid:
1116+ # x-numbers on the top row too, y-numbers ("units") on the right column.
1117+ ax .tick_params (labelbottom = True )
1118+ if col == 1 :
1119+ ax .tick_params (labelright = True , labelleft = False )
1120+ if col == 0 :
1121+ ax .set_ylabel ("AUC" )
1122+
1123+ # n_test annotation — top-right corner, clear of the 0.5 baseline
11081124 for run_id in run_ids :
11091125 if "shuffled" not in run_id :
11101126 res = _load (results_dir , run_id , probe )
11111127 if res :
1112- _annotate_n_test (ax , _n_test_from_results (res ))
1128+ n = _n_test_from_results (res )
1129+ s = _fmt_n (n )
1130+ if s :
1131+ ax .annotate (f"n = { s } $h_t$" ,
1132+ xy = (1 , 1 ), xycoords = "axes fraction" ,
1133+ xytext = (- 4 , - 4 ), textcoords = "offset points" ,
1134+ ha = "right" , va = "top" , fontsize = 7 ,
1135+ color = "#999999" , annotation_clip = False )
11131136 break
11141137
11151138 # x-label only on bottom row
@@ -1350,7 +1373,7 @@ def _compute_edit_stats(label_dir: Path, cache_path: Path | None = None) -> dict
13501373 continue
13511374 counts .append (sum (1 for e in d .get ("edits" , []) if e .get ("cmd_idx" ) != - 1 ))
13521375 if not counts :
1353- result = {"total" : 0 , "ge2" : 0 , "median" : 0 , "q25" : 0 , "q75" : 0 }
1376+ result = {"total" : 0 , "ge2" : 0 , "median" : 0 , "q25" : 0 , "q75" : 0 , "counts" : [] }
13541377 else :
13551378 s = sorted (counts )
13561379 n = len (s )
@@ -1360,6 +1383,7 @@ def _compute_edit_stats(label_dir: Path, cache_path: Path | None = None) -> dict
13601383 "median" : s [n // 2 ],
13611384 "q25" : s [n // 4 ],
13621385 "q75" : s [3 * n // 4 ],
1386+ "counts" : s , # raw per-trajectory edit counts, for pooled medians
13631387 }
13641388 if cache_path :
13651389 cache_path .parent .mkdir (parents = True , exist_ok = True )
@@ -1486,7 +1510,13 @@ def plot_dataset_turns(
14861510 ax .axhline (15 , color = "#555555" , linestyle = "--" , linewidth = 0.9 , label = "15 turns" )
14871511 ax .axhline (50 , color = "#222222" , linestyle = ":" , linewidth = 0.9 , label = "50 turns" )
14881512 ax .set_xticks (range (1 , len (all_labels ) + 1 ))
1489- ax .set_xticklabels (all_labels , fontsize = 8 )
1513+ # Shorten the model name (e.g. "Qwen3.6-35B-A3B" -> "Qwen3.6") so the
1514+ # horizontal tick labels don't overlap between adjacent ticks.
1515+ def _short_label (label : str ) -> str :
1516+ parts = label .split ("\n " )
1517+ parts [0 ] = parts [0 ].split ("-" )[0 ]
1518+ return "\n " .join (parts )
1519+ ax .set_xticklabels ([_short_label (l ) for l in all_labels ], fontsize = 8 )
14901520 ax .set_ylabel ("Turns" )
14911521 ax .set_ylim (top = 100 )
14921522 ax .yaxis .set_major_locator (MaxNLocator (integer = True ))
@@ -1619,8 +1649,10 @@ def build_dataset_stats_table(
16191649 r"\#Traj.\ counts all agent runs; "
16201650 r"$\geq$50 counts trajectories reaching that turn threshold "
16211651 r"(the length filter used in the lookahead experiments). "
1652+ r"Med.\ steps is the median number of turns per trajectory. "
16221653 r"\#Edits is the total number of code edits across all trajectories. "
16231654 r"$\geq$2 edits counts trajectories with at least two edits. "
1655+ r"Med.\ edits is the median number of edits per trajectory. "
16241656 r"$\#h_t$ is the total number of collected hidden-state vectors."
16251657 )
16261658
@@ -1636,14 +1668,16 @@ def _fmt_states(n: int) -> str:
16361668 r"\caption{" + caption + r"}" "\n "
16371669 r"\label{tab:dataset_stats}" "\n "
16381670 r"\resizebox{\columnwidth}{!}{" "\n "
1639- r"\begin{tabular}{llrrrrr }" "\n "
1671+ r"\begin{tabular}{llrrrrrrr }" "\n "
16401672 r"\toprule" "\n "
1641- r"Model & Benchmark & \#Traj. & $\geq$50 steps & \ #Edits & $\geq$2 edits & $\#h_t$ \\" "\n "
1673+ r"Model & Benchmark & \#Traj. & $\geq$50 steps & Med.\ steps & \ #Edits & $\geq$2 edits & Med.\ edits & $\#h_t$ \\" "\n "
16421674 r"\midrule" "\n "
16431675 )
16441676
16451677 rows = []
16461678 tot_traj = tot_gt50 = tot_ge2 = tot_edits = tot_states = 0
1679+ all_turns : list [int ] = [] # pooled across configs, for the total median
1680+ all_edits : list [int ] = []
16471681
16481682 i = 0
16491683 while i < len (configs ):
@@ -1657,33 +1691,45 @@ def _fmt_states(n: int) -> str:
16571691 mc = model_cell if first_in_group else ""
16581692 first_in_group = False
16591693 if cfg .get ("placeholder" ):
1660- rows .append (rf"{ mc } & { bm } & " + r"\multicolumn{5 }{c}{---} \\" )
1694+ rows .append (rf"{ mc } & { bm } & " + r"\multicolumn{7 }{c}{---} \\" )
16611695 else :
16621696 stats = cfg ["gen_stats" ]
16631697 n_traj = len (stats )
16641698 gt50 = sum (1 for s in stats if s ["turns" ] >= 50 )
1699+ turns = sorted (s ["turns" ] for s in stats )
1700+ med_steps = turns [len (turns ) // 2 ] if turns else 0
16651701 es = cfg ["edit_stats" ]
1702+ med_edits = es .get ("median" , 0 )
16661703 n_states = cfg ["n_states" ]
16671704
16681705 tot_traj += n_traj
16691706 tot_gt50 += gt50
16701707 tot_ge2 += es .get ("ge2" , 0 )
16711708 tot_edits += es ["total" ]
16721709 tot_states += n_states
1710+ all_turns .extend (s ["turns" ] for s in stats )
1711+ all_edits .extend (es .get ("counts" , []))
16731712
16741713 rows .append (
16751714 rf"{ mc } & { bm } & "
1676- rf"{ n_traj :,} & { gt50 :,} & { es ['total' ]:,} & { es .get ('ge2' , 0 ):,} & { _fmt_states (n_states )} \\"
1715+ rf"{ n_traj :,} & { gt50 :,} & { med_steps :, } & { es ['total' ]:,} & { es .get ('ge2' , 0 ):, } & { med_edits :,} & { _fmt_states (n_states )} \\"
16771716 )
16781717 if i + 2 < len (configs ):
16791718 rows .append (r"\addlinespace" )
16801719 i += 2
16811720
1682- # Total row
1721+ # Total row (medians pooled across all trajectories, not summed)
1722+ def _median (xs : list [int ]) -> str :
1723+ if not xs :
1724+ return "---"
1725+ s = sorted (xs )
1726+ return f"{ s [len (s ) // 2 ]:,} "
1727+
16831728 rows .append (r"\midrule" )
16841729 rows .append (
16851730 rf"\multicolumn{{2}}{{l}}{{Total}} & "
1686- rf"{ tot_traj :,} & { tot_gt50 :,} & { tot_edits :,} & { tot_ge2 :,} & { _fmt_states (tot_states )} \\"
1731+ rf"{ tot_traj :,} & { tot_gt50 :,} & { _median (all_turns )} & "
1732+ rf"{ tot_edits :,} & { tot_ge2 :,} & { _median (all_edits )} & { _fmt_states (tot_states )} \\"
16871733 )
16881734
16891735 body = "\n " .join (rows )
0 commit comments