import gradio as gr from gradio_leaderboard import Leaderboard, ColumnFilter, SelectColumns import pandas as pd import json import os from pathlib import Path from dataclasses import dataclass from enum import Enum from typing import List, Dict, Any # Define column structure for VRP results @dataclass class VRPColumn: name: str type: str displayed_by_default: bool = True hidden: bool = False never_hidden: bool = False # Define VRP result columns class VRPResultColumn: problem = VRPColumn("problem", "str", never_hidden=True) solver = VRPColumn("solver", "str", never_hidden=True) instance = VRPColumn("instance", "number") total_cost = VRPColumn("total_cost", "number") runtime = VRPColumn("runtime", "number") feasibility = VRPColumn("feasibility", "number") robustness = VRPColumn("robustness", "number") cvr = VRPColumn("cvr", "number") num_routes = VRPColumn("num_routes", "number") avg_route_length = VRPColumn("avg_route_length", "number") def fields(cls): """Get all fields from a class""" return [getattr(cls, attr) for attr in dir(cls) if not attr.startswith('__') and isinstance(getattr(cls, attr), VRPColumn)] def load_vrp_results(data_directory: str = "data") -> pd.DataFrame: """Load all VRP results from JSON files""" results = [] if not os.path.exists(data_directory): print(f"Data directory '{data_directory}' not found. Creating empty DataFrame.") return pd.DataFrame() # Get all JSON files in the data directory json_files = list(Path(data_directory).glob("*.json")) for json_file in json_files: try: with open(json_file, 'r') as f: data = json.load(f) # Extract basic information result = { 'problem': data.get('problem', ''), 'solver': data.get('solver', ''), 'instance': data.get('instance', 0), 'total_cost': data.get('metrics', {}).get('total_cost', 0), 'runtime': data.get('metrics', {}).get('runtime', 0), 'feasibility': data.get('metrics', {}).get('feasibility', 0), 'robustness': data.get('metrics', {}).get('robustness', 0), 'cvr': data.get('metrics', {}).get('cvr', 0), } # Calculate additional metrics routes = data.get('routes', []) result['num_routes'] = len(routes) # Calculate average route length (number of stops per route) if routes: total_stops = sum(len(route) for route in routes) result['avg_route_length'] = total_stops / len(routes) else: result['avg_route_length'] = 0 results.append(result) except Exception as e: print(f"Error loading {json_file}: {e}") df = pd.DataFrame(results) # Round numerical values for better display if not df.empty: numerical_cols = ['total_cost', 'runtime', 'robustness', 'avg_route_length'] for col in numerical_cols: if col in df.columns: df[col] = df[col].round(4) return df def get_problem_summary(df: pd.DataFrame) -> Dict[str, Any]: """Get summary statistics for the problems""" if df.empty: return {} summary = { 'total_experiments': len(df), 'unique_problems': df['problem'].nunique(), 'unique_solvers': df['solver'].nunique(), 'problems': df['problem'].unique().tolist(), 'solvers': df['solver'].unique().tolist(), } # Best performance by solver if 'total_cost' in df.columns: best_by_solver = df.groupby('solver')['total_cost'].agg(['mean', 'min', 'count']).round(2) summary['solver_performance'] = best_by_solver.to_dict('index') return summary def create_summary_text(summary: Dict[str, Any]) -> str: """Create summary text for display""" if not summary: return "No data loaded. Please ensure your JSON files are in the 'data' directory." text = f""" ## 📊 VRP Algorithm Leaderboard Summary **Total Experiments:** {summary.get('total_experiments', 0)} **Unique Problems:** {summary.get('unique_problems', 0)} **Algorithms Tested:** {summary.get('unique_solvers', 0)} ### Problems Evaluated: {', '.join(summary.get('problems', []))} ### Algorithms: {', '.join(summary.get('solvers', []))} """ if 'solver_performance' in summary: text += "\n### Algorithm Performance Summary (Total Cost):\n" for solver, stats in summary['solver_performance'].items(): text += f"- **{solver}**: Avg: {stats['mean']}, Best: {stats['min']}, Runs: {stats['count']}\n" return text def init_vrp_leaderboard(dataframe: pd.DataFrame): """Initialize the VRP leaderboard""" if dataframe is None or dataframe.empty: # Create empty dataframe with correct columns columns = [c.name for c in fields(VRPResultColumn)] dataframe = pd.DataFrame(columns=columns) return Leaderboard( value=dataframe, datatype=[c.type for c in fields(VRPResultColumn)], select_columns=SelectColumns( default_selection=[c.name for c in fields(VRPResultColumn) if c.displayed_by_default], cant_deselect=[c.name for c in fields(VRPResultColumn) if c.never_hidden], label="Select Columns to Display:", ), search_columns=[VRPResultColumn.problem.name, VRPResultColumn.solver.name], hide_columns=[c.name for c in fields(VRPResultColumn) if c.hidden], filter_columns=[ ColumnFilter(VRPResultColumn.solver.name, type="checkboxgroup", label="Algorithms"), ColumnFilter(VRPResultColumn.problem.name, type="checkboxgroup", label="Problem Types"), ColumnFilter( VRPResultColumn.total_cost.name, type="slider", min=0, max=10000, label="Total Cost Range", ), ColumnFilter( VRPResultColumn.runtime.name, type="slider", min=0, max=100, label="Runtime Range (seconds)", ), ColumnFilter( VRPResultColumn.feasibility.name, type="boolean", label="Feasible Solutions Only", default=False ), ], bool_checkboxgroup_label="Filter Options", interactive=False, ) # Custom CSS custom_css = """ .markdown-text { font-size: 16px !important; } #vrp-leaderboard-tab-table { margin-top: 0px !important; } .tab-buttons button { font-size: 20px; } """ # Load the data print("Loading VRP results...") LEADERBOARD_DF = load_vrp_results("data") SUMMARY = get_problem_summary(LEADERBOARD_DF) # Create the Gradio interface demo = gr.Blocks(css=custom_css, title="VRP Algorithm Leaderboard") with demo: gr.HTML("