1919import data_cache
2020
2121# Increase all font sizes by 16 points from their defaults
22- rcParams .update ({key : rcParams [key ] + 16 for key in rcParams if "size" in key and isinstance (rcParams [key ], (int , float ))})
22+ rcParams .update ({key : rcParams [key ] + 32 for key in rcParams if "size" in key and isinstance (rcParams [key ], (int , float ))})
2323
2424# ============================================================================
2525# CONFIGURATION SECTION
@@ -249,26 +249,25 @@ def generate_distribution_comparison(block_dir, zns_dir, distribution, metric, o
249249
250250 print (f"Found { len (block_runs )} block runs and { len (zns_runs )} ZNS runs" )
251251
252- # Create figure with 6 subplots (1 row x 6 columns) using GridSpec for custom widths
252+ # Create figure with 6 subplots (2 rows x 3 columns) using GridSpec for custom widths
253253 # Subplots are 2/5 original height, but all spacing preserved
254- num_subplots = len (RATIOS ) * len (CHUNK_SIZES ) # 2 ratios * 3 chunk sizes = 6
254+ num_ratios = len (RATIOS )
255+ num_chunk_sizes = len (CHUNK_SIZES )
255256
256- # Width ratios: 1077MiB subplots (indices 2 and 5 ) are half as wide since they have 2 boxes instead of 4
257- width_ratios = [1 , 1 , 0.5 , 1 , 1 , 0.5 ]
257+ # Width ratios: 1077MiB subplots (column 2 ) are half as wide since they have 2 boxes instead of 4
258+ width_ratios = [1 , 1 , 0.5 ]
258259
259- fig = plt .figure (figsize = (5 * num_subplots , 5.38 ))
260- gs = GridSpec (1 , num_subplots , figure = fig , width_ratios = width_ratios )
261- axes = [fig .add_subplot (gs [0 , i ]) for i in range (num_subplots )]
260+ fig = plt .figure (figsize = (8.0 * num_chunk_sizes , 7.0 * num_ratios ))
261+ gs = GridSpec (num_ratios , num_chunk_sizes , figure = fig , width_ratios = width_ratios )
262+ axes = [[ fig .add_subplot (gs [i , j ]) for j in range ( num_chunk_sizes )] for i in range (num_ratios )]
262263
263264 # First pass: collect all data to find global maximum for y-axis
264265 all_subplot_data = []
265266 global_max = 0.0
266267
267- idx = 0
268-
269268 # Iterate through ratios, then chunk sizes
270- for ratio in RATIOS :
271- for chunk_size in CHUNK_SIZES :
269+ for ratio_idx , ratio in enumerate ( RATIOS ) :
270+ for chunk_idx , chunk_size in enumerate ( CHUNK_SIZES ) :
272271 # Prepare data for this subplot
273272 current_data = []
274273 labels = []
@@ -364,7 +363,9 @@ def generate_distribution_comparison(block_dir, zns_dir, distribution, metric, o
364363 'labels' : labels ,
365364 'colors' : colors ,
366365 'hatches' : hatches ,
367- 'chunk_size' : chunk_size
366+ 'chunk_size' : chunk_size ,
367+ 'ratio_idx' : ratio_idx ,
368+ 'chunk_idx' : chunk_idx
368369 })
369370
370371 # Update global maximum if using common scale
@@ -375,8 +376,6 @@ def generate_distribution_comparison(block_dir, zns_dir, distribution, metric, o
375376 if local_max > global_max :
376377 global_max = local_max
377378
378- idx += 1
379-
380379 # Add some padding to the global max (10% above highest value)
381380 if common_y_scale :
382381 y_max = global_max * 1.1
@@ -385,13 +384,14 @@ def generate_distribution_comparison(block_dir, zns_dir, distribution, metric, o
385384 y_max = None
386385
387386 # Second pass: create boxplots with common y-axis
388- idx = 0
389387 for subplot_info in all_subplot_data :
390388 current_data = subplot_info ['data' ]
391389 labels = subplot_info ['labels' ]
392390 colors = subplot_info ['colors' ]
393391 hatches = subplot_info ['hatches' ]
394392 chunk_size = subplot_info ['chunk_size' ]
393+ ratio_idx = subplot_info ['ratio_idx' ]
394+ chunk_idx = subplot_info ['chunk_idx' ]
395395
396396 # Create boxplot for this subplot
397397 if current_data :
@@ -404,10 +404,13 @@ def generate_distribution_comparison(block_dir, zns_dir, distribution, metric, o
404404 else :
405405 box_width = 0.8 * (num_boxes / 4 ) if num_boxes > 0 else 0.8
406406
407- bp = axes [idx ].boxplot (current_data ,
407+ bp = axes [ratio_idx ][ chunk_idx ].boxplot (current_data ,
408408 showfliers = show_outliers ,
409409 widths = box_width ,
410- medianprops = dict (linewidth = 2 , color = 'black' ),
410+ boxprops = dict (linewidth = 3 ),
411+ whiskerprops = dict (linewidth = 3 ),
412+ capprops = dict (linewidth = 3 ),
413+ medianprops = dict (linewidth = 3 , color = 'black' ),
411414 patch_artist = True )
412415
413416 # Apply colors and hatches
@@ -418,46 +421,45 @@ def generate_distribution_comparison(block_dir, zns_dir, distribution, metric, o
418421 box .set_alpha (0.7 )
419422
420423 # Set x-axis labels (empty for cleaner look, or could add device labels)
421- axes [idx ].set_xticks (range (1 , len (labels ) + 1 ))
422- axes [idx ].set_xticklabels ([], rotation = 45 , fontsize = 10 )
424+ axes [ratio_idx ][ chunk_idx ].set_xticks (range (1 , len (labels ) + 1 ))
425+ axes [ratio_idx ][ chunk_idx ].set_xticklabels ([], rotation = 45 , fontsize = 10 )
423426
424427 # Add chunk size label below subplot
425- axes [idx ] .set_xlabel (CHUNK_SIZE_LABELS [chunk_size ], fontsize = 28 , weight = 'bold' )
428+ axes [ratio_idx ][ chunk_idx ] .set_xlabel (CHUNK_SIZE_LABELS [chunk_size ], fontsize = 58 , weight = 'bold' )
426429
427430 # Use scalar formatter without scientific notation
428- axes [idx ].yaxis .set_major_formatter (ticker .ScalarFormatter (useOffset = False , useMathText = False ))
431+ axes [ratio_idx ][ chunk_idx ].yaxis .set_major_formatter (ticker .ScalarFormatter (useOffset = False , useMathText = False ))
429432
430433 # Rotate y-axis labels
431- for label in axes [idx ].get_yticklabels ():
434+ for label in axes [ratio_idx ][ chunk_idx ].get_yticklabels ():
432435 label .set_rotation (45 )
433436
434437 # Set y-axis range
435438 if common_y_scale :
436439 # Use common y-axis maximum for all subplots
437- axes [idx ].set_ylim (0 , y_max )
440+ axes [ratio_idx ][ chunk_idx ].set_ylim (0 , y_max )
438441 else :
439442 # Just set bottom to 0, let matplotlib auto-scale the top
440- axes [idx ].set_ylim (bottom = 0 )
441-
442- idx += 1
443+ axes [ratio_idx ][chunk_idx ].set_ylim (bottom = 0 )
443444
444445 # Add y-axis label on the far left
445- fig .text (- 0.005 , 0.5 , metric_label , va = 'center' , rotation = 'vertical' , fontsize = 22 , weight = 'bold' )
446+ fig .text (- 0.065 , 0.5 , metric_label , va = 'center' , rotation = 'vertical' , fontsize = 64 , weight = 'bold' )
446447
447448 # Adjust layout (do these BEFORE computing positions) - subplots at 2/5 height with proportional spacing
448- plt .subplots_adjust (wspace = 0.2 , hspace = 0.0 )
449449 plt .tight_layout (pad = 0.0 )
450- plt .subplots_adjust (top = 0.851 , bottom = 0.279 , left = 0.05 )
450+ plt .subplots_adjust (top = 0.90 , bottom = 0.15 , left = 0.08 , hspace = 1.2 , wspace = 0.3 )
451451
452452 # Make sure layout is finalized
453453 fig .canvas .draw ()
454454
455455 # Compute positions for ratio boxes and labels based on actual subplot bounds
456- axes_bboxes = [ax .get_position ().bounds for ax in axes ] # (x, y, w, h) per axes
456+ # Flatten the 2D axes array to get all subplot bounds
457+ axes_flat = [axes [i ][j ] for i in range (num_ratios ) for j in range (num_chunk_sizes )]
458+ axes_bboxes = [ax .get_position ().bounds for ax in axes_flat ] # (x, y, w, h) per axes
457459
458- # First 3 subplots -> Ratio 1:2, next 3 -> Ratio 1:10
459- group1 = axes_bboxes [0 :3 ]
460- group2 = axes_bboxes [3 :6 ]
460+ # First row ( 3 subplots) -> Ratio 1:2, second row (3 subplots) -> Ratio 1:10
461+ group1 = axes_bboxes [0 :3 ] # First row
462+ group2 = axes_bboxes [3 :6 ] # Second row
461463
462464 # Left/right bounds of each group
463465 g1_left = group1 [0 ][0 ]
@@ -468,14 +470,21 @@ def generate_distribution_comparison(block_dir, zns_dir, distribution, metric, o
468470 g2_right = group2 [- 1 ][0 ] + group2 [- 1 ][2 ]
469471 g2_width = g2_right - g2_left
470472
471- # Vertical placement of the grey boxes in figure coords
472- box_y = 0.85
473- box_h = 0.10
473+ # Vertical placement of the grey boxes - position above each row
474+ # Get the top y position of each row's subplots and add some padding
475+ g1_top = group1 [0 ][1 ] + group1 [0 ][3 ] # y + height of first row
476+ g2_top = group2 [0 ][1 ] + group2 [0 ][3 ] # y + height of second row
477+
478+ box_h = 0.06
479+ box_y_offset = 0.02 # Space above the subplot
480+
481+ g1_box_y = g1_top + box_y_offset
482+ g2_box_y = g2_top + box_y_offset
474483
475- # Grey box for Ratio 1:2
484+ # Grey box for Ratio 1:2 (first row)
476485 fig .add_artist (
477486 Rectangle (
478- (g1_left , box_y ),
487+ (g1_left , g1_box_y ),
479488 g1_width ,
480489 box_h ,
481490 transform = fig .transFigure ,
@@ -487,10 +496,10 @@ def generate_distribution_comparison(block_dir, zns_dir, distribution, metric, o
487496 )
488497 )
489498
490- # Grey box for Ratio 1:10
499+ # Grey box for Ratio 1:10 (second row)
491500 fig .add_artist (
492501 Rectangle (
493- (g2_left , box_y ),
502+ (g2_left , g2_box_y ),
494503 g2_width ,
495504 box_h ,
496505 transform = fig .transFigure ,
@@ -505,21 +514,21 @@ def generate_distribution_comparison(block_dir, zns_dir, distribution, metric, o
505514 # Centered text in each box
506515 fig .text (
507516 g1_left + g1_width / 2 ,
508- box_y + box_h / 2 ,
517+ g1_box_y + box_h / 2 ,
509518 "Ratio: 1:2" ,
510519 ha = 'center' ,
511520 va = 'center' ,
512- fontsize = 26 ,
521+ fontsize = 50 ,
513522 weight = 'bold' ,
514523 zorder = 2 ,
515524 )
516525 fig .text (
517526 g2_left + g2_width / 2 ,
518- box_y + box_h / 2 ,
527+ g2_box_y + box_h / 2 ,
519528 "Ratio: 1:10" ,
520529 ha = 'center' ,
521530 va = 'center' ,
522- fontsize = 26 ,
531+ fontsize = 50 ,
523532 weight = 'bold' ,
524533 zorder = 2 ,
525534 )
@@ -533,9 +542,9 @@ def generate_distribution_comparison(block_dir, zns_dir, distribution, metric, o
533542 ]
534543
535544 fig .legend (
536- ncols = 4 ,
545+ ncols = 2 ,
537546 handles = legend_patches ,
538- bbox_to_anchor = (0.5 , 0.08 ),
547+ bbox_to_anchor = (0.5 , - 0.08 ),
539548 loc = 'center' ,
540549 fontsize = "large" ,
541550 columnspacing = 2.0 ,
0 commit comments