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("

🚗 SVRP Bench Leaderboard

") gr.Markdown(create_summary_text(SUMMARY), elem_classes="markdown-text") with gr.Tabs(elem_classes="tab-buttons") as tabs: with gr.TabItem("📊 Results Leaderboard", elem_id="vrp-leaderboard-tab-table", id=0): if not LEADERBOARD_DF.empty: leaderboard = init_vrp_leaderboard(LEADERBOARD_DF) else: gr.Markdown("### No data found. Please ensure your JSON files are in the 'data' directory.") with gr.TabItem("📈 Analysis", elem_id="vrp-analysis-tab", id=1): gr.Markdown(""" ## Algorithm Performance Analysis This leaderboard compares different algorithms for solving Vehicle Routing Problems: ### Key Metrics: - **Total Cost**: Lower is better - represents the total distance/cost of all routes - **Runtime**: Lower is better - time taken to solve the problem - **Feasibility**: Should be 1.0 - indicates if solution satisfies all constraints - **CVR (Constraint Violation Rate)**: Should be 0.0 - measures constraint violations - **Robustness**: Algorithm-specific robustness measure - **Number of Routes**: Total routes in the solution - **Average Route Length**: Average number of stops per route ### Algorithms Tested: - **ACO (Ant Colony Optimization)**: Bio-inspired metaheuristic - **NN+2opt (Nearest Neighbor + 2-opt)**: Constructive heuristic with local search - **OR-Tools**: Google's optimization toolkit - **Tabu Search**: Advanced local search metaheuristic """, elem_classes="markdown-text") with gr.TabItem("🔄 Refresh Data", elem_id="refresh-tab", id=2): gr.Markdown("Click the button below to reload data from JSON files:") def refresh_data(): global LEADERBOARD_DF, SUMMARY LEADERBOARD_DF = load_vrp_results("data") SUMMARY = get_problem_summary(LEADERBOARD_DF) if not LEADERBOARD_DF.empty: return LEADERBOARD_DF, create_summary_text(SUMMARY), "✅ Data refreshed successfully!" else: return LEADERBOARD_DF, create_summary_text(SUMMARY), "⚠️ No data found in 'data' directory" refresh_button = gr.Button("🔄 Refresh Data", variant="primary") refresh_status = gr.Markdown() # This would need to be connected to update the leaderboard # refresh_button.click(refresh_data, outputs=[refresh_status]) if __name__ == "__main__": demo.launch()