77import matplotlib .pyplot as plt
88import numpy as np
99import pandas as pd
10+ from matplotlib .ticker import AutoMinorLocator
1011
11- from codeclash .analysis .viz .utils import FONT_BOLD , FONT_REG , MODEL_TO_DISPLAY_NAME
12+ from codeclash .analysis .viz .utils import FONT_BOLD , MODEL_TO_DISPLAY_NAME
1213
1314
1415class GroundingValidationPlotter :
@@ -51,8 +52,10 @@ class GroundingValidationPlotter:
5152 ("forestgreen" , 0.3 ), # Simulations only
5253 ]
5354
54- def __init__ (self , df : pd .DataFrame ):
55+ def __init__ (self , df : pd .DataFrame , * , fix_xlim : bool = False , title : str | None = None ):
5556 self .df = df
57+ self .fix_xlim = fix_xlim
58+ self .title = title
5659
5760 # Aggregate hallucination columns by claim
5861 h_cols = [col for col in self .df .columns if col .startswith ("h_" )]
@@ -76,20 +79,20 @@ def __init__(self, df: pd.DataFrame):
7679 self .n_models = len (self .models )
7780
7881 # Create figure with 3 subplots sharing y-axis
79- self .fig , self .axes = plt .subplots (1 , 3 , figsize = (12 , 4 ), sharey = True )
82+ fig_height = 4.5 if self .title else 4
83+ self .fig , self .axes = plt .subplots (1 , 3 , figsize = (12 , fig_height ), sharey = True )
8084 self .y_positions = np .arange (self .n_models )
8185
8286 def _get_l_reason_breakdown (self , row ) -> str :
8387 """Categorize loss reason hallucinations."""
88+ if row ["h_loss_reason" ] == 0 :
89+ return ""
90+ if row ["h_loss_reason" ] == row ["h_loss_reason__none" ]:
91+ return "no source"
8492 c0 = ["h_loss_reason__log" , "h_loss_reason__execution_output.analysis" ]
8593 if any ([row [c ] > 0 for c in c0 ]):
8694 return "logs/analysis"
87- elif row ["h_loss_reason__none" ] > 0 :
88- if row ["h_loss_reason" ] > row ["h_loss_reason__none" ]:
89- # There's other cols that are also > 0
90- return "docs/tests/other"
91- return "no source"
92- return ""
95+ return "other"
9396
9497 def _add_stacked_bar_labels (self , ax , stacked_values : list [list [float ]], min_value_to_show : float = 5.0 ):
9598 """Add percentage labels inside stacked bars.
@@ -203,7 +206,7 @@ def plot_grounding_analysis(self):
203206 ax .set_title ("(a) Groundedness of edits" , fontproperties = font_title , pad = self .title_pad )
204207
205208 # Legend
206- font_legend = FONT_REG .copy ()
209+ font_legend = FONT_BOLD .copy ()
207210 font_legend .set_size (self .legend_fontsize )
208211 leg = ax .legend (
209212 frameon = False ,
@@ -216,21 +219,23 @@ def plot_grounding_analysis(self):
216219
217220 # Y-axis
218221 ax .set_yticks (self .y_positions )
219- font_ytick = FONT_REG .copy ()
222+ font_ytick = FONT_BOLD .copy ()
220223 font_ytick .set_size (self .ytick_label_fontsize )
221224 ax .set_yticklabels (self .models , fontproperties = font_ytick )
222225
223226 # X-axis
224- font_label = FONT_REG .copy ()
227+ font_label = FONT_BOLD .copy ()
225228 font_label .set_size (self .label_fontsize )
226229 ax .set_xlabel ("Percentage of rounds" , fontproperties = font_label )
227230 ax .tick_params (axis = "y" , length = 0 )
228231 ax .tick_params (axis = "x" , labelsize = self .xtick_label_fontsize )
229- font_xtick = FONT_REG .copy ()
232+ font_xtick = FONT_BOLD .copy ()
230233 font_xtick .set_size (self .xtick_label_fontsize )
231234 for label in ax .get_xticklabels ():
232235 label .set_fontproperties (font_xtick )
233- ax .xaxis .set_minor_locator (plt .MultipleLocator (5 ))
236+ ax .xaxis .set_minor_locator (AutoMinorLocator ())
237+ if self .fix_xlim :
238+ ax .set_xlim (0 , 100 )
234239 ax .spines ["top" ].set_visible (False )
235240 ax .spines ["right" ].set_visible (False )
236241
@@ -310,7 +315,7 @@ def plot_validation_feedback(self):
310315 ax .set_title ("(c) Validation of edits" , fontproperties = font_title , pad = self .title_pad )
311316
312317 # Legend
313- font_legend = FONT_REG .copy ()
318+ font_legend = FONT_BOLD .copy ()
314319 font_legend .set_size (self .legend_fontsize )
315320 leg = ax .legend (
316321 frameon = False ,
@@ -325,16 +330,18 @@ def plot_validation_feedback(self):
325330 leg .set_in_layout (False )
326331
327332 # X-axis
328- font_label = FONT_REG .copy ()
333+ font_label = FONT_BOLD .copy ()
329334 font_label .set_size (self .label_fontsize )
330335 ax .set_xlabel ("Percentage of rounds" , fontproperties = font_label )
331336 ax .tick_params (axis = "y" , length = 0 )
332337 ax .tick_params (axis = "x" , labelsize = self .xtick_label_fontsize )
333- font_xtick = FONT_REG .copy ()
338+ font_xtick = FONT_BOLD .copy ()
334339 font_xtick .set_size (self .xtick_label_fontsize )
335340 for label in ax .get_xticklabels ():
336341 label .set_fontproperties (font_xtick )
337- ax .xaxis .set_minor_locator (plt .MultipleLocator (5 ))
342+ ax .xaxis .set_minor_locator (AutoMinorLocator ())
343+ if self .fix_xlim :
344+ ax .set_xlim (0 , 100 )
338345 ax .spines ["top" ].set_visible (False )
339346 ax .spines ["right" ].set_visible (False )
340347
@@ -349,7 +356,7 @@ def plot_hallucination_categories(self):
349356 model_df = self .df [self .df ["model_name" ] == model ]
350357 total_rounds = len (model_df )
351358
352- mis_count = ((model_df ["hal_cat1" ] == "logs/analysis" ) | (model_df ["hal_cat1" ] == "docs/tests/ other" )).sum ()
359+ mis_count = ((model_df ["hal_cat1" ] == "logs/analysis" ) | (model_df ["hal_cat1" ] == "other" )).sum ()
353360 no_src_count = (model_df ["hal_cat1" ] == "no source" ).sum ()
354361
355362 misinterpretation .append ((mis_count / total_rounds * 100 ) if total_rounds > 0 else 0 )
@@ -394,7 +401,7 @@ def plot_hallucination_categories(self):
394401 ax .set_title ("(b) Hallucinated Loss Causality" , fontproperties = font_title , pad = self .title_pad )
395402
396403 # Legend
397- font_legend = FONT_REG .copy ()
404+ font_legend = FONT_BOLD .copy ()
398405 font_legend .set_size (self .legend_fontsize )
399406 leg = ax .legend (
400407 frameon = False ,
@@ -409,16 +416,18 @@ def plot_hallucination_categories(self):
409416 leg .set_in_layout (False )
410417
411418 # X-axis
412- font_label = FONT_REG .copy ()
419+ font_label = FONT_BOLD .copy ()
413420 font_label .set_size (self .label_fontsize )
414421 ax .set_xlabel ("Percentage of rounds" , fontproperties = font_label )
415422 ax .tick_params (axis = "y" , length = 0 )
416423 ax .tick_params (axis = "x" , labelsize = self .xtick_label_fontsize )
417- font_xtick = FONT_REG .copy ()
424+ font_xtick = FONT_BOLD .copy ()
418425 font_xtick .set_size (self .xtick_label_fontsize )
419426 for label in ax .get_xticklabels ():
420427 label .set_fontproperties (font_xtick )
421- ax .xaxis .set_minor_locator (plt .MultipleLocator (5 ))
428+ ax .xaxis .set_minor_locator (AutoMinorLocator ())
429+ if self .fix_xlim :
430+ ax .set_xlim (0 , 50 )
422431 ax .spines ["top" ].set_visible (False )
423432 ax .spines ["right" ].set_visible (False )
424433
@@ -427,6 +436,12 @@ def create_plot(self):
427436 self .plot_grounding_analysis ()
428437 self .plot_hallucination_categories ()
429438 self .plot_validation_feedback ()
439+
440+ if self .title :
441+ font_suptitle = FONT_BOLD .copy ()
442+ font_suptitle .set_size (18 )
443+ self .fig .suptitle (self .title , fontproperties = font_suptitle , y = 0.95 )
444+
430445 self .fig .tight_layout ()
431446
432447 def save (self , output_path : Path ):
@@ -472,7 +487,8 @@ def main():
472487 game_df = df [df ["game_name" ] == game_name ].copy ()
473488 game_output = output_dir / f"{ output_stem } _{ game_name } .pdf"
474489
475- plotter = GroundingValidationPlotter (game_df )
490+ display_title = "Poker" if game_name == "HuskyBench" else game_name
491+ plotter = GroundingValidationPlotter (game_df , fix_xlim = True , title = display_title )
476492 plotter .create_plot ()
477493 plotter .save (game_output )
478494
0 commit comments