Arpit-Bansal commited on
Commit
e356b9f
·
1 Parent(s): b13842d

all supported algos now in bencmarks/

Browse files
benchmarks/constraint_satisfaction/test_constraint_benchmark.py CHANGED
@@ -74,7 +74,7 @@ def main():
74
  )
75
 
76
  # Generate schedules using different methods
77
- methods = ['ga', 'pso', 'sa']
78
  schedules = []
79
 
80
  print(f"Generating {len(methods)} schedules using different optimization methods...")
 
74
  )
75
 
76
  # Generate schedules using different methods
77
+ methods = ['ga', 'pso', 'sa', 'cmaes', 'nsga2', 'adaptive', 'ensemble']
78
  schedules = []
79
 
80
  print(f"Generating {len(methods)} schedules using different optimization methods...")
benchmarks/optimizer_performance/benchmark_optimizers.py CHANGED
@@ -14,18 +14,71 @@ import os
14
  # Add project root to path
15
  sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')))
16
 
17
- from DataService.metro_data_generator import MetroDataGenerator
18
  from DataService.schedule_optimizer import MetroScheduleOptimizer
19
  from greedyOptim.scheduler import TrainsetSchedulingOptimizer
20
- from greedyOptim.genetic_algorithm import GeneticAlgorithmOptimizer
21
- from greedyOptim.ortools_optimizers import ORToolsOptimizer
22
- from greedyOptim.models import OptimizationConfig
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
 
25
  class OptimizerBenchmark:
26
  """Benchmark different optimization algorithms"""
27
 
28
- def __init__(self, num_trains: int = 25, num_stations: int = 22):
29
  self.results = {
30
  "benchmark_info": {
31
  "date": datetime.now().isoformat(),
@@ -34,78 +87,61 @@ class OptimizerBenchmark:
34
  "test_configurations": [],
35
  "results": []
36
  }
37
- self.data_generator = MetroDataGenerator(num_trains=num_trains, num_stations=num_stations)
38
 
39
- def generate_test_data(self):
40
  """Generate consistent test data for all optimizers"""
41
- route = self.data_generator.generate_route(
42
- route_name="Aluva-Pettah Line"
43
- )
44
-
45
- trains = self.data_generator.generate_train_health_statuses()
46
- # Limit to requested number
47
- trains = trains[:num_trains]
48
-
49
- return route, trains
50
 
51
  def benchmark_optimizer(
52
  self,
53
  optimizer_name: str,
54
- optimizer_class,
55
  num_trains: int,
56
- num_stations: int = 22,
57
  num_runs: int = 3
58
  ) -> Dict[str, Any]:
59
  """Benchmark a single optimizer"""
60
  print(f"\n{'='*70}")
61
  print(f"Benchmarking: {optimizer_name}")
62
- print(f"Fleet Size: {num_trains} trains | Stations: {num_stations}")
63
  print(f"{'='*70}")
64
 
65
  run_times = []
66
  success_count = 0
67
- schedules_generated = []
68
 
69
  for run in range(num_runs):
70
- print(f"\nRun {run + 1}/{num_runs}...", end=" ")
71
 
72
  try:
73
  # Generate fresh data for each run
74
- route, trains = self.generate_test_data(num_trains, num_stations)
75
-
76
- # Create request
77
- request = ScheduleRequest(
78
- date=date.today(),
79
- num_trains=num_trains,
80
- route=route,
81
- trains=trains
82
- )
83
 
84
  # Time the optimization
85
  start_time = time.perf_counter()
86
 
87
- optimizer = optimizer_class()
88
- schedule = optimizer.optimize(request)
89
 
90
  end_time = time.perf_counter()
91
  elapsed = end_time - start_time
92
 
93
  run_times.append(elapsed)
94
  success_count += 1
95
- schedules_generated.append(schedule)
96
 
97
- print(f"✓ Completed in {elapsed:.4f}s")
98
 
99
  except Exception as e:
100
- print(f"✗ Failed: {str(e)[:50]}")
101
- continue
 
102
 
103
  # Calculate statistics
104
  if run_times:
105
  result = {
106
  "optimizer": optimizer_name,
107
  "fleet_size": num_trains,
108
- "num_stations": num_stations,
109
  "num_runs": num_runs,
110
  "successful_runs": success_count,
111
  "success_rate": f"{(success_count/num_runs)*100:.1f}%",
@@ -113,17 +149,13 @@ class OptimizerBenchmark:
113
  "min_seconds": min(run_times),
114
  "max_seconds": max(run_times),
115
  "mean_seconds": statistics.mean(run_times),
116
- "median_seconds": statistics.median(run_times),
117
- "stdev_seconds": statistics.stdev(run_times) if len(run_times) > 1 else 0,
118
- "all_runs": run_times
119
- },
120
- "schedule_quality": self._analyze_schedules(schedules_generated) if schedules_generated else None
121
  }
122
  else:
123
  result = {
124
  "optimizer": optimizer_name,
125
  "fleet_size": num_trains,
126
- "num_stations": num_stations,
127
  "num_runs": num_runs,
128
  "successful_runs": 0,
129
  "success_rate": "0%",
@@ -134,45 +166,12 @@ class OptimizerBenchmark:
134
  print(f" Success Rate: {result['success_rate']}")
135
  if run_times:
136
  print(f" Average Time: {result['execution_times']['mean_seconds']:.4f}s")
137
- print(f" Std Dev: {result['execution_times']['stdev_seconds']:.4f}s")
138
 
139
  return result
140
-
141
- def _analyze_schedules(self, schedules: List) -> Dict[str, Any]:
142
- """Analyze quality metrics of generated schedules"""
143
- if not schedules:
144
- return None
145
-
146
- total_trips_list = []
147
- trains_used_list = []
148
-
149
- for schedule in schedules:
150
- if hasattr(schedule, 'trips'):
151
- total_trips_list.append(len(schedule.trips))
152
- if hasattr(schedule, 'train_schedules'):
153
- trains_used_list.append(len(schedule.train_schedules))
154
-
155
- quality = {}
156
-
157
- if total_trips_list:
158
- quality["trips"] = {
159
- "mean": statistics.mean(total_trips_list),
160
- "min": min(total_trips_list),
161
- "max": max(total_trips_list)
162
- }
163
-
164
- if trains_used_list:
165
- quality["trains_utilized"] = {
166
- "mean": statistics.mean(trains_used_list),
167
- "min": min(trains_used_list),
168
- "max": max(trains_used_list)
169
- }
170
-
171
- return quality if quality else None
172
-
173
  def run_comprehensive_benchmark(
174
  self,
175
- fleet_sizes: List[int] = [5, 10, 15, 20, 25, 30],
176
  num_runs: int = 3
177
  ):
178
  """Run comprehensive benchmark across all optimizers and fleet sizes"""
@@ -185,19 +184,14 @@ class OptimizerBenchmark:
185
 
186
  # Define optimizers to test
187
  optimizers = [
188
- ("Greedy Optimizer", GreedyScheduleOptimizer),
189
- ("Genetic Algorithm", GeneticScheduleOptimizer),
190
- ("OR-Tools CP-SAT", ORToolsScheduleOptimizer),
191
- ]
192
-
193
- # Store test configurations
194
- self.results["test_configurations"] = [
195
- {
196
- "fleet_sizes": fleet_sizes,
197
- "num_stations": 22,
198
- "runs_per_config": num_runs,
199
- "optimizers": [name for name, _ in optimizers]
200
- }
201
  ]
202
 
203
  # Run benchmarks
@@ -206,10 +200,10 @@ class OptimizerBenchmark:
206
  print(f"# FLEET SIZE: {fleet_size} TRAINS")
207
  print(f"{'#'*70}")
208
 
209
- for optimizer_name, optimizer_class in optimizers:
210
  result = self.benchmark_optimizer(
211
  optimizer_name,
212
- optimizer_class,
213
  fleet_size,
214
  num_runs=num_runs
215
  )
@@ -220,6 +214,13 @@ class OptimizerBenchmark:
220
 
221
  # Generate comparison summary
222
  self._generate_summary()
 
 
 
 
 
 
 
223
 
224
  def _generate_summary(self):
225
  """Generate comparative summary of results"""
@@ -249,183 +250,112 @@ class OptimizerBenchmark:
249
  avg_time = result["execution_times"]["mean_seconds"] if "execution_times" in result else "N/A"
250
  success = result["success_rate"]
251
 
252
- if avg_time != "N/A":
253
- print(f"{optimizer:<25} {avg_time:<15.4f} {success:<15}")
 
 
 
 
 
 
254
  fleet_summary.append({
255
  "optimizer": optimizer,
256
- "avg_time": avg_time,
257
- "success_rate": success
258
  })
259
- else:
260
- print(f"{optimizer:<25} {'FAILED':<15} {success:<15}")
261
 
 
 
262
  summary["by_fleet_size"][fleet_size] = fleet_summary
 
 
 
 
 
 
 
263
 
264
- # Overall performance ranking
265
  print("\n" + "="*70)
266
- print("OVERALL PERFORMANCE RANKING")
267
  print("="*70)
268
-
269
- optimizer_avg_times = {}
270
- for result in self.results["results"]:
271
- if "execution_times" in result:
272
- optimizer = result["optimizer"]
273
- if optimizer not in optimizer_avg_times:
274
- optimizer_avg_times[optimizer] = []
275
- optimizer_avg_times[optimizer].append(result["execution_times"]["mean_seconds"])
276
-
277
- rankings = []
278
- for optimizer, times in optimizer_avg_times.items():
279
- avg = statistics.mean(times)
280
- rankings.append((optimizer, avg))
281
-
282
- rankings.sort(key=lambda x: x[1])
283
-
284
- print(f"\n{'Rank':<8} {'Optimizer':<25} {'Avg Time (s)':<15}")
285
  print("-" * 70)
286
- for rank, (optimizer, avg_time) in enumerate(rankings, 1):
287
- print(f"{rank:<8} {optimizer:<25} {avg_time:<15.4f}")
288
- summary["overall_rankings"][optimizer] = {
289
- "rank": rank,
290
- "avg_time_seconds": avg_time
291
- }
292
 
293
- self.results["summary"] = summary
294
-
295
- def save_results(self, filename: str = None):
296
- """Save benchmark results to JSON file"""
297
- if filename is None:
298
- filename = f"benchmark_results_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
 
299
 
300
- with open(filename, 'w') as f:
301
- json.dump(self.results, f, indent=2, default=str)
302
 
303
- print(f"\n{'='*70}")
304
- print(f"Results saved to: {filename}")
305
- print(f"{'='*70}")
306
 
307
- return filename
308
-
309
- def generate_performance_report(self):
310
- """Generate a formatted performance report"""
311
- report_filename = f"performance_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
312
 
313
- with open(report_filename, 'w') as f:
314
- f.write("="*80 + "\n")
315
- f.write("METRO SCHEDULE OPTIMIZER PERFORMANCE REPORT\n")
316
- f.write("="*80 + "\n\n")
317
- f.write(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
318
-
319
- # Executive Summary
320
- f.write("EXECUTIVE SUMMARY\n")
321
- f.write("-"*80 + "\n")
322
- if "summary" in self.results and "overall_rankings" in self.results["summary"]:
323
- f.write("\nOverall Performance Ranking:\n")
324
- for optimizer, data in self.results["summary"]["overall_rankings"].items():
325
- f.write(f" {data['rank']}. {optimizer}: {data['avg_time_seconds']:.4f}s average\n")
326
- f.write("\n")
327
-
328
- # Detailed Results
329
- f.write("\nDETAILED RESULTS BY FLEET SIZE\n")
330
- f.write("-"*80 + "\n\n")
331
 
332
- for result in self.results["results"]:
333
- f.write(f"Optimizer: {result['optimizer']}\n")
334
- f.write(f"Fleet Size: {result['fleet_size']} trains\n")
335
- f.write(f"Success Rate: {result['success_rate']}\n")
 
336
 
337
- if "execution_times" in result:
338
- f.write(f"Execution Time:\n")
339
- f.write(f" Mean: {result['execution_times']['mean_seconds']:.4f}s\n")
340
- f.write(f" Median: {result['execution_times']['median_seconds']:.4f}s\n")
341
- f.write(f" Min: {result['execution_times']['min_seconds']:.4f}s\n")
342
- f.write(f" Max: {result['execution_times']['max_seconds']:.4f}s\n")
343
- f.write(f" StdDev: {result['execution_times']['stdev_seconds']:.4f}s\n")
344
-
345
- if result.get("schedule_quality"):
346
- f.write(f"Schedule Quality:\n")
347
- if "trips" in result["schedule_quality"]:
348
- f.write(f" Trips Generated (avg): {result['schedule_quality']['trips']['mean']:.1f}\n")
349
- if "trains_utilized" in result["schedule_quality"]:
350
- f.write(f" Trains Utilized (avg): {result['schedule_quality']['trains_utilized']['mean']:.1f}\n")
351
-
352
- f.write("\n" + "-"*80 + "\n\n")
353
-
354
- # Recommendations
355
- f.write("\nRECOMMENDATIONS\n")
356
- f.write("-"*80 + "\n")
357
- f.write("Based on the benchmark results:\n\n")
358
-
359
- if "summary" in self.results and "overall_rankings" in self.results["summary"]:
360
- rankings = sorted(
361
- self.results["summary"]["overall_rankings"].items(),
362
- key=lambda x: x[1]["rank"]
363
- )
364
- if rankings:
365
- fastest = rankings[0]
366
- f.write(f"• {fastest[0]} showed the best overall performance\n")
367
- f.write(f" with an average execution time of {fastest[1]['avg_time_seconds']:.4f}s\n\n")
368
 
369
- f.write(" Consider using faster optimizers for real-time scheduling\n")
370
- f.write(" Slower optimizers may provide better solution quality for offline planning\n")
371
- f.write(" Test with your specific constraints and requirements\n\n")
372
-
373
- print(f"Performance report saved to: {report_filename}")
374
- return report_filename
375
-
376
 
377
  def main():
378
- """Main execution function"""
379
  import argparse
380
-
381
  parser = argparse.ArgumentParser(description="Benchmark metro schedule optimizers")
382
- parser.add_argument(
383
- "--fleet-sizes",
384
- nargs="+",
385
- type=int,
386
- default=[5, 10, 15, 20, 25, 30],
387
- help="Fleet sizes to test (default: 5 10 15 20 25 30)"
388
- )
389
- parser.add_argument(
390
- "--runs",
391
- type=int,
392
- default=3,
393
- help="Number of runs per configuration (default: 3)"
394
- )
395
- parser.add_argument(
396
- "--quick",
397
- action="store_true",
398
- help="Quick test with fewer configurations"
399
- )
400
 
401
  args = parser.parse_args()
402
 
403
  if args.quick:
404
- fleet_sizes = [10, 20, 30]
405
- num_runs = 2
406
  print("\n*** QUICK BENCHMARK MODE ***")
 
 
407
  else:
408
  fleet_sizes = args.fleet_sizes
409
- num_runs = args.runs
410
 
411
- # Run benchmark
412
  benchmark = OptimizerBenchmark()
413
  benchmark.run_comprehensive_benchmark(
414
  fleet_sizes=fleet_sizes,
415
- num_runs=num_runs
416
  )
417
-
418
- # Save results
419
- json_file = benchmark.save_results()
420
- report_file = benchmark.generate_performance_report()
421
-
422
- print("\n" + "="*70)
423
- print("BENCHMARK COMPLETE")
424
- print("="*70)
425
- print(f"JSON Results: {json_file}")
426
- print(f"Text Report: {report_file}")
427
- print("="*70 + "\n")
428
-
429
 
430
  if __name__ == "__main__":
431
  main()
 
14
  # Add project root to path
15
  sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')))
16
 
17
+ from DataService.enhanced_generator import EnhancedMetroDataGenerator
18
  from DataService.schedule_optimizer import MetroScheduleOptimizer
19
  from greedyOptim.scheduler import TrainsetSchedulingOptimizer
20
+ from DataService.metro_models import Route, TrainHealthStatus
21
+
22
+ # --- Adapters for Uniform Interface ---
23
+
24
+ class OptimizerAdapter:
25
+ """Base adapter for different optimizers"""
26
+ def optimize(self, data: Dict) -> Any:
27
+ raise NotImplementedError
28
+
29
+ class GeneticAdapter(OptimizerAdapter):
30
+ """Adapter for Genetic Algorithm"""
31
+ def optimize(self, data: Dict) -> Any:
32
+ optimizer = TrainsetSchedulingOptimizer(data)
33
+ return optimizer.optimize(method='ga')
34
+
35
+ class PSOAdapter(OptimizerAdapter):
36
+ """Adapter for Particle Swarm Optimization"""
37
+ def optimize(self, data: Dict) -> Any:
38
+ optimizer = TrainsetSchedulingOptimizer(data)
39
+ return optimizer.optimize(method='pso')
40
+
41
+ class SAAdapter(OptimizerAdapter):
42
+ """Adapter for Simulated Annealing"""
43
+ def optimize(self, data: Dict) -> Any:
44
+ optimizer = TrainsetSchedulingOptimizer(data)
45
+ return optimizer.optimize(method='sa')
46
+
47
+ class CMAESAdapter(OptimizerAdapter):
48
+ """Adapter for CMA-ES"""
49
+ def optimize(self, data: Dict) -> Any:
50
+ optimizer = TrainsetSchedulingOptimizer(data)
51
+ return optimizer.optimize(method='cmaes')
52
+
53
+ class NSGA2Adapter(OptimizerAdapter):
54
+ """Adapter for NSGA-II"""
55
+ def optimize(self, data: Dict) -> Any:
56
+ optimizer = TrainsetSchedulingOptimizer(data)
57
+ return optimizer.optimize(method='nsga2')
58
+
59
+ class AdaptiveAdapter(OptimizerAdapter):
60
+ """Adapter for Adaptive Algorithm"""
61
+ def optimize(self, data: Dict) -> Any:
62
+ optimizer = TrainsetSchedulingOptimizer(data)
63
+ return optimizer.optimize(method='adaptive')
64
+
65
+ class EnsembleAdapter(OptimizerAdapter):
66
+ """Adapter for Ensemble Method"""
67
+ def optimize(self, data: Dict) -> Any:
68
+ optimizer = TrainsetSchedulingOptimizer(data)
69
+ return optimizer.optimize(method='ensemble')
70
+
71
+ class ORToolsAdapter(OptimizerAdapter):
72
+ """Adapter for OR-Tools CP-SAT"""
73
+ def optimize(self, data: Dict) -> Any:
74
+ optimizer = TrainsetSchedulingOptimizer(data)
75
+ return optimizer.optimize(method='cp-sat')
76
 
77
 
78
  class OptimizerBenchmark:
79
  """Benchmark different optimization algorithms"""
80
 
81
+ def __init__(self):
82
  self.results = {
83
  "benchmark_info": {
84
  "date": datetime.now().isoformat(),
 
87
  "test_configurations": [],
88
  "results": []
89
  }
 
90
 
91
+ def generate_test_data(self, num_trains: int) -> Dict:
92
  """Generate consistent test data for all optimizers"""
93
+ generator = EnhancedMetroDataGenerator(num_trainsets=num_trains)
94
+ # We need the full dataset as expected by TrainsetSchedulingEvaluator
95
+ full_data = generator.generate_complete_enhanced_dataset()
96
+ return full_data
 
 
 
 
 
97
 
98
  def benchmark_optimizer(
99
  self,
100
  optimizer_name: str,
101
+ adapter_class,
102
  num_trains: int,
 
103
  num_runs: int = 3
104
  ) -> Dict[str, Any]:
105
  """Benchmark a single optimizer"""
106
  print(f"\n{'='*70}")
107
  print(f"Benchmarking: {optimizer_name}")
108
+ print(f"Fleet Size: {num_trains} trains")
109
  print(f"{'='*70}")
110
 
111
  run_times = []
112
  success_count = 0
 
113
 
114
  for run in range(num_runs):
115
+ print(f"Run {run + 1}/{num_runs}...", end=" ", flush=True)
116
 
117
  try:
118
  # Generate fresh data for each run
119
+ data = self.generate_test_data(num_trains)
 
 
 
 
 
 
 
 
120
 
121
  # Time the optimization
122
  start_time = time.perf_counter()
123
 
124
+ adapter = adapter_class()
125
+ result = adapter.optimize(data)
126
 
127
  end_time = time.perf_counter()
128
  elapsed = end_time - start_time
129
 
130
  run_times.append(elapsed)
131
  success_count += 1
 
132
 
133
+ print(f"✓ Completed in {elapsed:.4f}s | Fitness: {result.fitness_score:.2f}")
134
 
135
  except Exception as e:
136
+ print(f"✗ Failed: {str(e)[:100]}")
137
+ # import traceback
138
+ # traceback.print_exc()
139
 
140
  # Calculate statistics
141
  if run_times:
142
  result = {
143
  "optimizer": optimizer_name,
144
  "fleet_size": num_trains,
 
145
  "num_runs": num_runs,
146
  "successful_runs": success_count,
147
  "success_rate": f"{(success_count/num_runs)*100:.1f}%",
 
149
  "min_seconds": min(run_times),
150
  "max_seconds": max(run_times),
151
  "mean_seconds": statistics.mean(run_times),
152
+ "stdev_seconds": statistics.stdev(run_times) if len(run_times) > 1 else 0
153
+ }
 
 
 
154
  }
155
  else:
156
  result = {
157
  "optimizer": optimizer_name,
158
  "fleet_size": num_trains,
 
159
  "num_runs": num_runs,
160
  "successful_runs": 0,
161
  "success_rate": "0%",
 
166
  print(f" Success Rate: {result['success_rate']}")
167
  if run_times:
168
  print(f" Average Time: {result['execution_times']['mean_seconds']:.4f}s")
 
169
 
170
  return result
171
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  def run_comprehensive_benchmark(
173
  self,
174
+ fleet_sizes: List[int] = [10, 20, 30],
175
  num_runs: int = 3
176
  ):
177
  """Run comprehensive benchmark across all optimizers and fleet sizes"""
 
184
 
185
  # Define optimizers to test
186
  optimizers = [
187
+ ("Genetic Algorithm", GeneticAdapter),
188
+ ("Particle Swarm", PSOAdapter),
189
+ ("Simulated Annealing", SAAdapter),
190
+ ("CMA-ES", CMAESAdapter),
191
+ ("NSGA-II", NSGA2Adapter),
192
+ ("Adaptive Algorithm", AdaptiveAdapter),
193
+ ("Ensemble Method", EnsembleAdapter),
194
+ # ("OR-Tools CP-SAT", ORToolsAdapter), # Uncomment if OR-Tools is installed
 
 
 
 
 
195
  ]
196
 
197
  # Run benchmarks
 
200
  print(f"# FLEET SIZE: {fleet_size} TRAINS")
201
  print(f"{'#'*70}")
202
 
203
+ for optimizer_name, adapter_class in optimizers:
204
  result = self.benchmark_optimizer(
205
  optimizer_name,
206
+ adapter_class,
207
  fleet_size,
208
  num_runs=num_runs
209
  )
 
214
 
215
  # Generate comparison summary
216
  self._generate_summary()
217
+
218
+ # Save results
219
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
220
+ filename = f"optimizer_benchmark_{timestamp}.json"
221
+ with open(filename, 'w') as f:
222
+ json.dump(self.results, f, indent=2)
223
+ print(f"\nResults saved to: {filename}")
224
 
225
  def _generate_summary(self):
226
  """Generate comparative summary of results"""
 
250
  avg_time = result["execution_times"]["mean_seconds"] if "execution_times" in result else "N/A"
251
  success = result["success_rate"]
252
 
253
+ if isinstance(avg_time, float):
254
+ time_str = f"{avg_time:.4f}"
255
+ else:
256
+ time_str = str(avg_time)
257
+
258
+ print(f"{optimizer:<25} {time_str:<15} {success:<15}")
259
+
260
+ if isinstance(avg_time, float):
261
  fleet_summary.append({
262
  "optimizer": optimizer,
263
+ "time": avg_time
 
264
  })
 
 
265
 
266
+ # Rank for this fleet size
267
+ fleet_summary.sort(key=lambda x: x["time"])
268
  summary["by_fleet_size"][fleet_size] = fleet_summary
269
+
270
+ # Update overall stats
271
+ for item in fleet_summary:
272
+ opt = item["optimizer"]
273
+ if opt not in summary["overall_rankings"]:
274
+ summary["overall_rankings"][opt] = []
275
+ summary["overall_rankings"][opt].append(item["time"])
276
 
277
+ # Print overall rankings
278
  print("\n" + "="*70)
279
+ print("OVERALL PERFORMANCE RANKINGS (by average time)")
280
  print("="*70)
281
+ print(f"{'Rank':<8} {'Optimizer/Method':<30} {'Avg Time (s)':<15}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
  print("-" * 70)
 
 
 
 
 
 
283
 
284
+ overall_stats = []
285
+ for opt, times in summary["overall_rankings"].items():
286
+ if times:
287
+ overall_stats.append({
288
+ "optimizer": opt,
289
+ "avg_time": statistics.mean(times)
290
+ })
291
 
292
+ overall_stats.sort(key=lambda x: x["avg_time"])
 
293
 
294
+ for i, stat in enumerate(overall_stats):
295
+ print(f"{i+1:<8} {stat['optimizer']:<30} {stat['avg_time']:.4f}")
 
296
 
297
+ # Save report to text file
298
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
299
+ report_file = f"optimizer_performance_report_{timestamp}.txt"
 
 
300
 
301
+ with open(report_file, "w") as f:
302
+ f.write("OPTIMIZER PERFORMANCE BENCHMARK REPORT\n")
303
+ f.write(f"Date: {datetime.now().isoformat()}\n")
304
+ f.write("="*70 + "\n\n")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
305
 
306
+ for fleet_size in fleet_sizes:
307
+ f.write(f"Fleet Size: {fleet_size} trains\n")
308
+ f.write("-" * 70 + "\n")
309
+ f.write(f"{'Optimizer':<25} {'Avg Time (s)':<15} {'Success Rate':<15}\n")
310
+ f.write("-" * 70 + "\n")
311
 
312
+ fleet_results = [r for r in self.results["results"] if r["fleet_size"] == fleet_size]
313
+ for result in fleet_results:
314
+ optimizer = result["optimizer"]
315
+ avg_time = result["execution_times"]["mean_seconds"] if "execution_times" in result else "N/A"
316
+ success = result["success_rate"]
317
+
318
+ if isinstance(avg_time, float):
319
+ time_str = f"{avg_time:.4f}"
320
+ else:
321
+ time_str = str(avg_time)
322
+
323
+ f.write(f"{optimizer:<25} {time_str:<15} {success:<15}\n")
324
+ f.write("\n")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
325
 
326
+ f.write("="*70 + "\n")
327
+ f.write("OVERALL RANKINGS\n")
328
+ f.write("="*70 + "\n")
329
+ for i, stat in enumerate(overall_stats):
330
+ f.write(f"{i+1}. {stat['optimizer']}: {stat['avg_time']:.4f}s\n")
331
+
332
+ print(f"\nPerformance report saved to: {report_file}")
333
 
334
  def main():
 
335
  import argparse
 
336
  parser = argparse.ArgumentParser(description="Benchmark metro schedule optimizers")
337
+ parser.add_argument("--fleet-sizes", type=int, nargs="+", default=[10, 20, 30],
338
+ help="Fleet sizes to test (default: 10 20 30)")
339
+ parser.add_argument("--runs", type=int, default=3,
340
+ help="Number of runs per configuration (default: 3)")
341
+ parser.add_argument("--quick", action="store_true",
342
+ help="Quick test with fewer configurations")
 
 
 
 
 
 
 
 
 
 
 
 
343
 
344
  args = parser.parse_args()
345
 
346
  if args.quick:
 
 
347
  print("\n*** QUICK BENCHMARK MODE ***")
348
+ fleet_sizes = [10, 20]
349
+ runs = 1
350
  else:
351
  fleet_sizes = args.fleet_sizes
352
+ runs = args.runs
353
 
 
354
  benchmark = OptimizerBenchmark()
355
  benchmark.run_comprehensive_benchmark(
356
  fleet_sizes=fleet_sizes,
357
+ num_runs=runs
358
  )
 
 
 
 
 
 
 
 
 
 
 
 
359
 
360
  if __name__ == "__main__":
361
  main()
benchmarks/optimizer_performance/benchmark_schedule_performance.py CHANGED
@@ -152,7 +152,7 @@ class SchedulePerformanceBenchmark:
152
  def benchmark_greedy_optimizers(
153
  self,
154
  num_trains: int,
155
- methods: List[str] = ['ga', 'cmaes', 'pso'],
156
  num_runs: int = 3
157
  ) -> Dict[str, List[Dict[str, Any]]]:
158
  """Benchmark greedyOptim methods"""
@@ -451,8 +451,8 @@ def main():
451
  parser.add_argument(
452
  "--methods",
453
  nargs="+",
454
- default=['ga', 'cmaes', 'pso'],
455
- help="Greedy optimization methods to test (default: ga cmaes pso)"
456
  )
457
  parser.add_argument(
458
  "--runs",
 
152
  def benchmark_greedy_optimizers(
153
  self,
154
  num_trains: int,
155
+ methods: List[str] = ['ga', 'cmaes', 'pso', 'sa', 'nsga2', 'adaptive', 'ensemble'],
156
  num_runs: int = 3
157
  ) -> Dict[str, List[Dict[str, Any]]]:
158
  """Benchmark greedyOptim methods"""
 
451
  parser.add_argument(
452
  "--methods",
453
  nargs="+",
454
+ default=['ga', 'cmaes', 'pso', 'sa', 'nsga2', 'adaptive', 'ensemble'],
455
+ help="Greedy optimization methods to test (default: ga cmaes pso sa nsga2 adaptive ensemble)"
456
  )
457
  parser.add_argument(
458
  "--runs",
benchmarks/service_quality/benchmark_service_quality.py CHANGED
@@ -1,214 +0,0 @@
1
- """
2
- Service Quality Benchmark Script
3
- Runs comprehensive benchmarks for headway consistency, wait times, and coverage.
4
- """
5
- import time
6
- import json
7
- from typing import Dict, List
8
- from datetime import datetime
9
-
10
- from .service_analyzer import ServiceQualityAnalyzer, ServiceQualityMetrics
11
-
12
-
13
- def run_service_quality_benchmark(
14
- schedules: List[Dict],
15
- output_file: str = "service_quality_benchmark_results.json",
16
- verbose: bool = True
17
- ) -> Dict:
18
- """Run service quality benchmark on multiple schedules.
19
-
20
- Args:
21
- schedules: List of schedule dictionaries to analyze
22
- output_file: Path to save results JSON
23
- verbose: Whether to print detailed output
24
-
25
- Returns:
26
- Dictionary with benchmark results
27
- """
28
- if verbose:
29
- print("=" * 80)
30
- print("SERVICE QUALITY BENCHMARK")
31
- print("=" * 80)
32
- print(f"Analyzing {len(schedules)} schedules...")
33
- print()
34
-
35
- analyzer = ServiceQualityAnalyzer()
36
- start_time = time.time()
37
-
38
- results = []
39
-
40
- # Analyze each schedule
41
- for i, schedule in enumerate(schedules, 1):
42
- if verbose:
43
- print(f"Schedule {i}/{len(schedules)}:")
44
-
45
- schedule_start = time.time()
46
- metrics = analyzer.analyze_schedule(schedule)
47
- analysis_time = (time.time() - schedule_start) * 1000 # ms
48
-
49
- result = {
50
- 'schedule_id': i,
51
- 'analysis_time_ms': round(analysis_time, 2),
52
- 'metrics': {
53
- 'headway_consistency': {
54
- 'peak_mean_minutes': round(metrics.peak_headway_mean, 2),
55
- 'peak_std_minutes': round(metrics.peak_headway_std, 2),
56
- 'peak_cv': round(metrics.peak_headway_coefficient_variation, 3),
57
- 'offpeak_mean_minutes': round(metrics.offpeak_headway_mean, 2),
58
- 'offpeak_std_minutes': round(metrics.offpeak_headway_std, 2),
59
- 'offpeak_cv': round(metrics.offpeak_headway_coefficient_variation, 3),
60
- 'score': round(metrics.headway_consistency_score, 2)
61
- },
62
- 'wait_times': {
63
- 'avg_wait_peak_minutes': round(metrics.avg_wait_time_peak, 2),
64
- 'max_wait_peak_minutes': round(metrics.max_wait_time_peak, 2),
65
- 'avg_wait_offpeak_minutes': round(metrics.avg_wait_time_offpeak, 2),
66
- 'max_wait_offpeak_minutes': round(metrics.max_wait_time_offpeak, 2),
67
- 'reduction_vs_baseline_percent': round(metrics.wait_time_reduction_vs_baseline, 2),
68
- 'score': round(metrics.wait_time_score, 2)
69
- },
70
- 'service_coverage': {
71
- 'operational_hours': round(metrics.operational_hours, 2),
72
- 'peak_hours_covered': round(metrics.peak_hours_covered, 2),
73
- 'offpeak_hours_covered': round(metrics.offpeak_hours_covered, 2),
74
- 'coverage_percent': round(metrics.service_coverage_percent, 2),
75
- 'peak_coverage_percent': round(metrics.peak_coverage_percent, 2),
76
- 'service_gaps': metrics.gaps_in_service,
77
- 'score': round(metrics.coverage_score, 2)
78
- },
79
- 'overall_quality_score': round(metrics.overall_quality_score, 2)
80
- }
81
- }
82
-
83
- results.append(result)
84
-
85
- if verbose:
86
- print(f" Headway Consistency Score: {result['metrics']['headway_consistency']['score']:.2f}/100")
87
- print(f" Peak: {metrics.peak_headway_mean:.1f}min ± {metrics.peak_headway_std:.1f}min (CV: {metrics.peak_headway_coefficient_variation:.3f})")
88
- print(f" Off-Peak: {metrics.offpeak_headway_mean:.1f}min ± {metrics.offpeak_headway_std:.1f}min (CV: {metrics.offpeak_headway_coefficient_variation:.3f})")
89
- print(f" Wait Time Score: {result['metrics']['wait_times']['score']:.2f}/100")
90
- print(f" Peak avg wait: {metrics.avg_wait_time_peak:.1f}min (max: {metrics.max_wait_time_peak:.1f}min)")
91
- print(f" Off-peak avg wait: {metrics.avg_wait_time_offpeak:.1f}min (max: {metrics.max_wait_time_offpeak:.1f}min)")
92
- print(f" Improvement vs baseline: {metrics.wait_time_reduction_vs_baseline:.1f}%")
93
- print(f" Coverage Score: {result['metrics']['service_coverage']['score']:.2f}/100")
94
- print(f" Peak coverage: {metrics.peak_coverage_percent:.1f}%")
95
- print(f" Overall coverage: {metrics.service_coverage_percent:.1f}%")
96
- print(f" Service gaps: {metrics.gaps_in_service}")
97
- print(f" OVERALL QUALITY: {metrics.overall_quality_score:.2f}/100")
98
- print(f" Analysis time: {analysis_time:.2f}ms")
99
- print()
100
-
101
- total_time = time.time() - start_time
102
-
103
- # Calculate aggregate statistics
104
- aggregate = _calculate_aggregate_stats(results)
105
-
106
- # Prepare final output
107
- benchmark_results = {
108
- 'benchmark_info': {
109
- 'timestamp': datetime.now().isoformat(),
110
- 'total_schedules': len(schedules),
111
- 'total_time_seconds': round(total_time, 3),
112
- 'avg_analysis_time_ms': round(aggregate['avg_analysis_time_ms'], 2)
113
- },
114
- 'aggregate_metrics': aggregate,
115
- 'individual_results': results
116
- }
117
-
118
- # Print summary
119
- if verbose:
120
- print("=" * 80)
121
- print("AGGREGATE RESULTS")
122
- print("=" * 80)
123
- print(f"Schedules analyzed: {len(schedules)}")
124
- print(f"Total benchmark time: {total_time:.2f}s")
125
- print()
126
- print("Average Scores:")
127
- print(f" Headway Consistency: {aggregate['avg_headway_score']:.2f}/100")
128
- print(f" Wait Time Quality: {aggregate['avg_wait_score']:.2f}/100")
129
- print(f" Service Coverage: {aggregate['avg_coverage_score']:.2f}/100")
130
- print(f" Overall Quality: {aggregate['avg_overall_score']:.2f}/100")
131
- print()
132
- print("Best Performers:")
133
- print(f" Best headway consistency: Schedule {aggregate['best_headway_schedule']} ({aggregate['best_headway_score']:.2f})")
134
- print(f" Best wait times: Schedule {aggregate['best_wait_schedule']} ({aggregate['best_wait_score']:.2f})")
135
- print(f" Best coverage: Schedule {aggregate['best_coverage_schedule']} ({aggregate['best_coverage_score']:.2f})")
136
- print(f" Best overall: Schedule {aggregate['best_overall_schedule']} ({aggregate['best_overall_score']:.2f})")
137
- print()
138
- print("Service Quality Metrics:")
139
- print(f" Avg peak headway: {aggregate['avg_peak_headway']:.2f} ± {aggregate['avg_peak_headway_std']:.2f} minutes")
140
- print(f" Avg off-peak headway: {aggregate['avg_offpeak_headway']:.2f} ± {aggregate['avg_offpeak_headway_std']:.2f} minutes")
141
- print(f" Avg peak wait time: {aggregate['avg_peak_wait']:.2f} minutes")
142
- print(f" Avg wait time reduction: {aggregate['avg_wait_reduction']:.1f}%")
143
- print(f" Avg service coverage: {aggregate['avg_coverage_percent']:.1f}%")
144
- print(f" Avg service gaps: {aggregate['avg_service_gaps']:.1f}")
145
- print()
146
-
147
- # Save to file
148
- with open(output_file, 'w') as f:
149
- json.dump(benchmark_results, f, indent=2)
150
-
151
- if verbose:
152
- print(f"Results saved to: {output_file}")
153
- print("=" * 80)
154
-
155
- return benchmark_results
156
-
157
-
158
- def _calculate_aggregate_stats(results: List[Dict]) -> Dict:
159
- """Calculate aggregate statistics across all results."""
160
- if not results:
161
- return {}
162
-
163
- # Extract all metrics
164
- headway_scores = [r['metrics']['headway_consistency']['score'] for r in results]
165
- wait_scores = [r['metrics']['wait_times']['score'] for r in results]
166
- coverage_scores = [r['metrics']['service_coverage']['score'] for r in results]
167
- overall_scores = [r['metrics']['overall_quality_score'] for r in results]
168
-
169
- peak_headways = [r['metrics']['headway_consistency']['peak_mean_minutes'] for r in results]
170
- peak_headway_stds = [r['metrics']['headway_consistency']['peak_std_minutes'] for r in results]
171
- offpeak_headways = [r['metrics']['headway_consistency']['offpeak_mean_minutes'] for r in results]
172
- offpeak_headway_stds = [r['metrics']['headway_consistency']['offpeak_std_minutes'] for r in results]
173
- peak_waits = [r['metrics']['wait_times']['avg_wait_peak_minutes'] for r in results]
174
- wait_reductions = [r['metrics']['wait_times']['reduction_vs_baseline_percent'] for r in results]
175
- coverage_percents = [r['metrics']['service_coverage']['coverage_percent'] for r in results]
176
- service_gaps = [r['metrics']['service_coverage']['service_gaps'] for r in results]
177
- analysis_times = [r['analysis_time_ms'] for r in results]
178
-
179
- # Find best performers
180
- best_headway_idx = headway_scores.index(max(headway_scores))
181
- best_wait_idx = wait_scores.index(max(wait_scores))
182
- best_coverage_idx = coverage_scores.index(max(coverage_scores))
183
- best_overall_idx = overall_scores.index(max(overall_scores))
184
-
185
- return {
186
- 'avg_headway_score': round(sum(headway_scores) / len(headway_scores), 2),
187
- 'avg_wait_score': round(sum(wait_scores) / len(wait_scores), 2),
188
- 'avg_coverage_score': round(sum(coverage_scores) / len(coverage_scores), 2),
189
- 'avg_overall_score': round(sum(overall_scores) / len(overall_scores), 2),
190
-
191
- 'best_headway_schedule': best_headway_idx + 1,
192
- 'best_headway_score': round(max(headway_scores), 2),
193
- 'best_wait_schedule': best_wait_idx + 1,
194
- 'best_wait_score': round(max(wait_scores), 2),
195
- 'best_coverage_schedule': best_coverage_idx + 1,
196
- 'best_coverage_score': round(max(coverage_scores), 2),
197
- 'best_overall_schedule': best_overall_idx + 1,
198
- 'best_overall_score': round(max(overall_scores), 2),
199
-
200
- 'avg_peak_headway': round(sum(peak_headways) / len(peak_headways), 2),
201
- 'avg_peak_headway_std': round(sum(peak_headway_stds) / len(peak_headway_stds), 2),
202
- 'avg_offpeak_headway': round(sum(offpeak_headways) / len(offpeak_headways), 2),
203
- 'avg_offpeak_headway_std': round(sum(offpeak_headway_stds) / len(offpeak_headway_stds), 2),
204
- 'avg_peak_wait': round(sum(peak_waits) / len(peak_waits), 2),
205
- 'avg_wait_reduction': round(sum(wait_reductions) / len(wait_reductions), 2),
206
- 'avg_coverage_percent': round(sum(coverage_percents) / len(coverage_percents), 2),
207
- 'avg_service_gaps': round(sum(service_gaps) / len(service_gaps), 2),
208
- 'avg_analysis_time_ms': round(sum(analysis_times) / len(analysis_times), 2)
209
- }
210
-
211
-
212
- if __name__ == "__main__":
213
- print("Service Quality Benchmark Module")
214
- print("Import and use run_service_quality_benchmark() to analyze schedules")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
benchmarks/service_quality/test_service_quality_benchmark.py CHANGED
@@ -1,7 +1,8 @@
1
  """
2
- Test Service Quality Benchmark
3
- Direct test of the service quality benchmarking system.
4
  """
 
5
  import sys
6
  import os
7
 
@@ -12,7 +13,212 @@ from DataService.enhanced_generator import EnhancedMetroDataGenerator
12
  from greedyOptim.scheduler import TrainsetSchedulingOptimizer
13
  from greedyOptim.models import OptimizationConfig
14
  from greedyOptim.service_blocks import create_service_blocks_for_schedule
15
- from benchmarks.service_quality import run_service_quality_benchmark
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
 
18
  def create_schedule_with_service_blocks(result, method_name):
@@ -76,7 +282,7 @@ def main():
76
  )
77
 
78
  # Generate schedules using different methods
79
- methods = ['ga', 'pso', 'sa']
80
  schedules = []
81
 
82
  print(f"Generating {len(methods)} schedules using different optimization methods...")
 
1
  """
2
+ Service Quality Benchmark Script
3
+ Runs comprehensive benchmarks for headway consistency, wait times, and coverage.
4
  """
5
+
6
  import sys
7
  import os
8
 
 
13
  from greedyOptim.scheduler import TrainsetSchedulingOptimizer
14
  from greedyOptim.models import OptimizationConfig
15
  from greedyOptim.service_blocks import create_service_blocks_for_schedule
16
+
17
+ import time
18
+ import json
19
+ from typing import Dict, List
20
+ from datetime import datetime
21
+
22
+ from .service_analyzer import ServiceQualityAnalyzer, ServiceQualityMetrics
23
+
24
+
25
+ def run_service_quality_benchmark(
26
+ schedules: List[Dict],
27
+ output_file: str = "service_quality_benchmark_results.json",
28
+ verbose: bool = True
29
+ ) -> Dict:
30
+ """Run service quality benchmark on multiple schedules.
31
+
32
+ Args:
33
+ schedules: List of schedule dictionaries to analyze
34
+ output_file: Path to save results JSON
35
+ verbose: Whether to print detailed output
36
+
37
+ Returns:
38
+ Dictionary with benchmark results
39
+ """
40
+ if verbose:
41
+ print("=" * 80)
42
+ print("SERVICE QUALITY BENCHMARK")
43
+ print("=" * 80)
44
+ print(f"Analyzing {len(schedules)} schedules...")
45
+ print()
46
+
47
+ analyzer = ServiceQualityAnalyzer()
48
+ start_time = time.time()
49
+
50
+ results = []
51
+
52
+ # Analyze each schedule
53
+ for i, schedule in enumerate(schedules, 1):
54
+ if verbose:
55
+ print(f"Schedule {i}/{len(schedules)}:")
56
+
57
+ schedule_start = time.time()
58
+ metrics = analyzer.analyze_schedule(schedule)
59
+ analysis_time = (time.time() - schedule_start) * 1000 # ms
60
+
61
+ result = {
62
+ 'schedule_id': i,
63
+ 'analysis_time_ms': round(analysis_time, 2),
64
+ 'metrics': {
65
+ 'headway_consistency': {
66
+ 'peak_mean_minutes': round(metrics.peak_headway_mean, 2),
67
+ 'peak_std_minutes': round(metrics.peak_headway_std, 2),
68
+ 'peak_cv': round(metrics.peak_headway_coefficient_variation, 3),
69
+ 'offpeak_mean_minutes': round(metrics.offpeak_headway_mean, 2),
70
+ 'offpeak_std_minutes': round(metrics.offpeak_headway_std, 2),
71
+ 'offpeak_cv': round(metrics.offpeak_headway_coefficient_variation, 3),
72
+ 'score': round(metrics.headway_consistency_score, 2)
73
+ },
74
+ 'wait_times': {
75
+ 'avg_wait_peak_minutes': round(metrics.avg_wait_time_peak, 2),
76
+ 'max_wait_peak_minutes': round(metrics.max_wait_time_peak, 2),
77
+ 'avg_wait_offpeak_minutes': round(metrics.avg_wait_time_offpeak, 2),
78
+ 'max_wait_offpeak_minutes': round(metrics.max_wait_time_offpeak, 2),
79
+ 'reduction_vs_baseline_percent': round(metrics.wait_time_reduction_vs_baseline, 2),
80
+ 'score': round(metrics.wait_time_score, 2)
81
+ },
82
+ 'service_coverage': {
83
+ 'operational_hours': round(metrics.operational_hours, 2),
84
+ 'peak_hours_covered': round(metrics.peak_hours_covered, 2),
85
+ 'offpeak_hours_covered': round(metrics.offpeak_hours_covered, 2),
86
+ 'coverage_percent': round(metrics.service_coverage_percent, 2),
87
+ 'peak_coverage_percent': round(metrics.peak_coverage_percent, 2),
88
+ 'service_gaps': metrics.gaps_in_service,
89
+ 'score': round(metrics.coverage_score, 2)
90
+ },
91
+ 'overall_quality_score': round(metrics.overall_quality_score, 2)
92
+ }
93
+ }
94
+
95
+ results.append(result)
96
+
97
+ if verbose:
98
+ print(f" Headway Consistency Score: {result['metrics']['headway_consistency']['score']:.2f}/100")
99
+ print(f" Peak: {metrics.peak_headway_mean:.1f}min ± {metrics.peak_headway_std:.1f}min (CV: {metrics.peak_headway_coefficient_variation:.3f})")
100
+ print(f" Off-Peak: {metrics.offpeak_headway_mean:.1f}min ± {metrics.offpeak_headway_std:.1f}min (CV: {metrics.offpeak_headway_coefficient_variation:.3f})")
101
+ print(f" Wait Time Score: {result['metrics']['wait_times']['score']:.2f}/100")
102
+ print(f" Peak avg wait: {metrics.avg_wait_time_peak:.1f}min (max: {metrics.max_wait_time_peak:.1f}min)")
103
+ print(f" Off-peak avg wait: {metrics.avg_wait_time_offpeak:.1f}min (max: {metrics.max_wait_time_offpeak:.1f}min)")
104
+ print(f" Improvement vs baseline: {metrics.wait_time_reduction_vs_baseline:.1f}%")
105
+ print(f" Coverage Score: {result['metrics']['service_coverage']['score']:.2f}/100")
106
+ print(f" Peak coverage: {metrics.peak_coverage_percent:.1f}%")
107
+ print(f" Overall coverage: {metrics.service_coverage_percent:.1f}%")
108
+ print(f" Service gaps: {metrics.gaps_in_service}")
109
+ print(f" OVERALL QUALITY: {metrics.overall_quality_score:.2f}/100")
110
+ print(f" Analysis time: {analysis_time:.2f}ms")
111
+ print()
112
+
113
+ total_time = time.time() - start_time
114
+
115
+ # Calculate aggregate statistics
116
+ aggregate = _calculate_aggregate_stats(results)
117
+
118
+ # Prepare final output
119
+ benchmark_results = {
120
+ 'benchmark_info': {
121
+ 'timestamp': datetime.now().isoformat(),
122
+ 'total_schedules': len(schedules),
123
+ 'total_time_seconds': round(total_time, 3),
124
+ 'avg_analysis_time_ms': round(aggregate['avg_analysis_time_ms'], 2)
125
+ },
126
+ 'aggregate_metrics': aggregate,
127
+ 'individual_results': results
128
+ }
129
+
130
+ # Print summary
131
+ if verbose:
132
+ print("=" * 80)
133
+ print("AGGREGATE RESULTS")
134
+ print("=" * 80)
135
+ print(f"Schedules analyzed: {len(schedules)}")
136
+ print(f"Total benchmark time: {total_time:.2f}s")
137
+ print()
138
+ print("Average Scores:")
139
+ print(f" Headway Consistency: {aggregate['avg_headway_score']:.2f}/100")
140
+ print(f" Wait Time Quality: {aggregate['avg_wait_score']:.2f}/100")
141
+ print(f" Service Coverage: {aggregate['avg_coverage_score']:.2f}/100")
142
+ print(f" Overall Quality: {aggregate['avg_overall_score']:.2f}/100")
143
+ print()
144
+ print("Best Performers:")
145
+ print(f" Best headway consistency: Schedule {aggregate['best_headway_schedule']} ({aggregate['best_headway_score']:.2f})")
146
+ print(f" Best wait times: Schedule {aggregate['best_wait_schedule']} ({aggregate['best_wait_score']:.2f})")
147
+ print(f" Best coverage: Schedule {aggregate['best_coverage_schedule']} ({aggregate['best_coverage_score']:.2f})")
148
+ print(f" Best overall: Schedule {aggregate['best_overall_schedule']} ({aggregate['best_overall_score']:.2f})")
149
+ print()
150
+ print("Service Quality Metrics:")
151
+ print(f" Avg peak headway: {aggregate['avg_peak_headway']:.2f} ± {aggregate['avg_peak_headway_std']:.2f} minutes")
152
+ print(f" Avg off-peak headway: {aggregate['avg_offpeak_headway']:.2f} ± {aggregate['avg_offpeak_headway_std']:.2f} minutes")
153
+ print(f" Avg peak wait time: {aggregate['avg_peak_wait']:.2f} minutes")
154
+ print(f" Avg wait time reduction: {aggregate['avg_wait_reduction']:.1f}%")
155
+ print(f" Avg service coverage: {aggregate['avg_coverage_percent']:.1f}%")
156
+ print(f" Avg service gaps: {aggregate['avg_service_gaps']:.1f}")
157
+ print()
158
+
159
+ # Save to file
160
+ with open(output_file, 'w') as f:
161
+ json.dump(benchmark_results, f, indent=2)
162
+
163
+ if verbose:
164
+ print(f"Results saved to: {output_file}")
165
+ print("=" * 80)
166
+
167
+ return benchmark_results
168
+
169
+
170
+ def _calculate_aggregate_stats(results: List[Dict]) -> Dict:
171
+ """Calculate aggregate statistics across all results."""
172
+ if not results:
173
+ return {}
174
+
175
+ # Extract all metrics
176
+ headway_scores = [r['metrics']['headway_consistency']['score'] for r in results]
177
+ wait_scores = [r['metrics']['wait_times']['score'] for r in results]
178
+ coverage_scores = [r['metrics']['service_coverage']['score'] for r in results]
179
+ overall_scores = [r['metrics']['overall_quality_score'] for r in results]
180
+
181
+ peak_headways = [r['metrics']['headway_consistency']['peak_mean_minutes'] for r in results]
182
+ peak_headway_stds = [r['metrics']['headway_consistency']['peak_std_minutes'] for r in results]
183
+ offpeak_headways = [r['metrics']['headway_consistency']['offpeak_mean_minutes'] for r in results]
184
+ offpeak_headway_stds = [r['metrics']['headway_consistency']['offpeak_std_minutes'] for r in results]
185
+ peak_waits = [r['metrics']['wait_times']['avg_wait_peak_minutes'] for r in results]
186
+ wait_reductions = [r['metrics']['wait_times']['reduction_vs_baseline_percent'] for r in results]
187
+ coverage_percents = [r['metrics']['service_coverage']['coverage_percent'] for r in results]
188
+ service_gaps = [r['metrics']['service_coverage']['service_gaps'] for r in results]
189
+ analysis_times = [r['analysis_time_ms'] for r in results]
190
+
191
+ # Find best performers
192
+ best_headway_idx = headway_scores.index(max(headway_scores))
193
+ best_wait_idx = wait_scores.index(max(wait_scores))
194
+ best_coverage_idx = coverage_scores.index(max(coverage_scores))
195
+ best_overall_idx = overall_scores.index(max(overall_scores))
196
+
197
+ return {
198
+ 'avg_headway_score': round(sum(headway_scores) / len(headway_scores), 2),
199
+ 'avg_wait_score': round(sum(wait_scores) / len(wait_scores), 2),
200
+ 'avg_coverage_score': round(sum(coverage_scores) / len(coverage_scores), 2),
201
+ 'avg_overall_score': round(sum(overall_scores) / len(overall_scores), 2),
202
+
203
+ 'best_headway_schedule': best_headway_idx + 1,
204
+ 'best_headway_score': round(max(headway_scores), 2),
205
+ 'best_wait_schedule': best_wait_idx + 1,
206
+ 'best_wait_score': round(max(wait_scores), 2),
207
+ 'best_coverage_schedule': best_coverage_idx + 1,
208
+ 'best_coverage_score': round(max(coverage_scores), 2),
209
+ 'best_overall_schedule': best_overall_idx + 1,
210
+ 'best_overall_score': round(max(overall_scores), 2),
211
+
212
+ 'avg_peak_headway': round(sum(peak_headways) / len(peak_headways), 2),
213
+ 'avg_peak_headway_std': round(sum(peak_headway_stds) / len(peak_headway_stds), 2),
214
+ 'avg_offpeak_headway': round(sum(offpeak_headways) / len(offpeak_headways), 2),
215
+ 'avg_offpeak_headway_std': round(sum(offpeak_headway_stds) / len(offpeak_headway_stds), 2),
216
+ 'avg_peak_wait': round(sum(peak_waits) / len(peak_waits), 2),
217
+ 'avg_wait_reduction': round(sum(wait_reductions) / len(wait_reductions), 2),
218
+ 'avg_coverage_percent': round(sum(coverage_percents) / len(coverage_percents), 2),
219
+ 'avg_service_gaps': round(sum(service_gaps) / len(service_gaps), 2),
220
+ 'avg_analysis_time_ms': round(sum(analysis_times) / len(analysis_times), 2)
221
+ }
222
 
223
 
224
  def create_schedule_with_service_blocks(result, method_name):
 
282
  )
283
 
284
  # Generate schedules using different methods
285
+ methods = ['ga', 'pso', 'sa', 'cmaes', 'nsga2', 'adaptive', 'ensemble']
286
  schedules = []
287
 
288
  print(f"Generating {len(methods)} schedules using different optimization methods...")
greedyOptim/hybrid_optimizers.py CHANGED
@@ -237,9 +237,16 @@ class AdaptiveOptimizer:
237
  improvements[name] = 0
238
 
239
  # Update probabilities (softmax-like)
240
- total_improvement = sum(improvements.values()) + 1e-6
 
241
  for name in self.optimizers.keys():
242
- self.selection_probabilities[name] = (improvements[name] + 0.1) / (total_improvement + 0.4)
 
 
 
 
 
 
243
 
244
  def select_optimizer(self) -> str:
245
  """Select optimizer based on adaptive probabilities."""
 
237
  improvements[name] = 0
238
 
239
  # Update probabilities (softmax-like)
240
+ # Calculate raw weights
241
+ weights = {}
242
  for name in self.optimizers.keys():
243
+ weights[name] = improvements[name] + 0.1
244
+
245
+ total_weight = sum(weights.values())
246
+
247
+ # Normalize
248
+ for name in self.optimizers.keys():
249
+ self.selection_probabilities[name] = weights[name] / total_weight
250
 
251
  def select_optimizer(self) -> str:
252
  """Select optimizer based on adaptive probabilities."""