Commit ·
03df314
1
Parent(s): 8a3c708
resolved data-validation issues
Browse files- BENCHMARKING_GUIDE.md +34 -0
- BENCHMARK_FIX_SUMMARY.md +87 -0
- DataService/enhanced_generator.py +1 -1
- LICENSE +0 -0
- README.md +0 -191
- api/greedyoptim_api.py +18 -4
- api/test_greedyoptim_api.py +7 -7
- benchmark_optimizers.py +431 -0
- benchmark_schedule_performance.py +498 -0
- benchmarks/__init__.py +10 -0
- benchmarks/fleet_utilization/README.md +324 -0
- benchmarks/fleet_utilization/__init__.py +25 -0
- benchmarks/fleet_utilization/benchmark_fleet_utilization.py +331 -0
- benchmarks/fleet_utilization/fleet_analyzer.py +401 -0
- example_benchmark.py +56 -0
- greedyOptim/error_handling.py +1 -1
- schedule_benchmark_20251106_134014.json +838 -0
- schedule_performance_report_20251106_134014.txt +383 -0
BENCHMARKING_GUIDE.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Metro Schedule Generation - Benchmarking Guide
|
| 2 |
+
|
| 3 |
+
This guide explains how to use the comprehensive benchmarking system to measure schedule generation performance for your research paper.
|
| 4 |
+
|
| 5 |
+
## Overview
|
| 6 |
+
|
| 7 |
+
The benchmark system measures:
|
| 8 |
+
1. **Schedule Generation Time**: How long it takes to generate schedules for different fleet sizes
|
| 9 |
+
2. **Computational Efficiency**: Performance comparison between different optimization methods
|
| 10 |
+
|
| 11 |
+
## Data Generation
|
| 12 |
+
|
| 13 |
+
The benchmark uses **EnhancedMetroDataGenerator** from DataService to create complete, realistic synthetic data including:
|
| 14 |
+
- Trainset status and operational data
|
| 15 |
+
- Fitness certificates
|
| 16 |
+
- Job cards and maintenance records
|
| 17 |
+
- Component health monitoring
|
| 18 |
+
- IoT sensor data
|
| 19 |
+
- Performance metrics
|
| 20 |
+
|
| 21 |
+
This ensures that greedy optimizers are tested with realistic, complete datasets.
|
| 22 |
+
|
| 23 |
+
## Components Tested
|
| 24 |
+
|
| 25 |
+
### 1. MetroScheduleOptimizer (DataService)
|
| 26 |
+
- Primary scheduling system
|
| 27 |
+
- Fast, deterministic schedule generation
|
| 28 |
+
- Uses route-based optimization
|
| 29 |
+
|
| 30 |
+
### 2. Greedy Optimization Methods (greedyOptim)
|
| 31 |
+
- **GA** (Genetic Algorithm): Evolutionary optimization
|
| 32 |
+
- **CMA-ES**: Covariance Matrix Adaptation
|
| 33 |
+
- **PSO** (Particle Swarm): Swarm intelligence
|
| 34 |
+
- **SA** (Simulated Annealing): Probabilistic optimization
|
BENCHMARK_FIX_SUMMARY.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Benchmark Fix Summary
|
| 2 |
+
|
| 3 |
+
## Issue Fixed
|
| 4 |
+
The greedyOptim methods (GA, PSO, CMA-ES, etc.) were failing with error: `'trainset_status'`
|
| 5 |
+
|
| 6 |
+
## Root Cause
|
| 7 |
+
The benchmark was creating incomplete synthetic data with only:
|
| 8 |
+
```python
|
| 9 |
+
synthetic_data = {
|
| 10 |
+
"trainsets": [...], # Wrong key name!
|
| 11 |
+
"depot_layout": ...,
|
| 12 |
+
"date": ...
|
| 13 |
+
}
|
| 14 |
+
```
|
| 15 |
+
|
| 16 |
+
But the `TrainsetSchedulingEvaluator` in greedyOptim expects a **complete dataset** with:
|
| 17 |
+
- `trainset_status` (not "trainsets")
|
| 18 |
+
- `fitness_certificates`
|
| 19 |
+
- `job_cards`
|
| 20 |
+
- `component_health`
|
| 21 |
+
- `iot_sensors`
|
| 22 |
+
- `branding_contracts`
|
| 23 |
+
- `maintenance_schedule`
|
| 24 |
+
- `performance_metrics`
|
| 25 |
+
- And more...
|
| 26 |
+
|
| 27 |
+
## Solution Applied
|
| 28 |
+
✅ **Now using `EnhancedMetroDataGenerator` from DataService**
|
| 29 |
+
|
| 30 |
+
The benchmark now properly generates complete synthetic data:
|
| 31 |
+
|
| 32 |
+
```python
|
| 33 |
+
from DataService.enhanced_generator import EnhancedMetroDataGenerator
|
| 34 |
+
|
| 35 |
+
# Generate complete, realistic synthetic data
|
| 36 |
+
generator = EnhancedMetroDataGenerator(num_trainsets=num_trains)
|
| 37 |
+
synthetic_data = generator.generate_complete_enhanced_dataset()
|
| 38 |
+
```
|
| 39 |
+
|
| 40 |
+
This creates all required data structures that the greedyOptim evaluator needs.
|
| 41 |
+
|
| 42 |
+
## Files Modified
|
| 43 |
+
1. **benchmark_schedule_performance.py**
|
| 44 |
+
- Added import for `EnhancedMetroDataGenerator`
|
| 45 |
+
- Replaced manual data creation with proper generator call
|
| 46 |
+
- Added progress messages showing data generation stats
|
| 47 |
+
|
| 48 |
+
2. **example_benchmark.py**
|
| 49 |
+
- Added note about data generation method
|
| 50 |
+
- Updated comments
|
| 51 |
+
|
| 52 |
+
3. **BENCHMARKING_GUIDE.md**
|
| 53 |
+
- Added "Data Generation" section explaining the approach
|
| 54 |
+
|
| 55 |
+
## Testing
|
| 56 |
+
✅ Tested with command:
|
| 57 |
+
```bash
|
| 58 |
+
python benchmark_schedule_performance.py --fleet-sizes 10 --methods ga --runs 2
|
| 59 |
+
```
|
| 60 |
+
|
| 61 |
+
Result: **Successfully completed** - greedy optimizers now run without errors!
|
| 62 |
+
|
| 63 |
+
## Benefits
|
| 64 |
+
1. ✅ **Realistic Testing**: Greedy optimizers tested with complete, realistic data
|
| 65 |
+
2. ✅ **Consistency**: Same data generation used across all benchmarks
|
| 66 |
+
3. ✅ **Maintainability**: Leverages existing DataService infrastructure
|
| 67 |
+
4. ✅ **Accuracy**: Results reflect real-world performance with full datasets
|
| 68 |
+
|
| 69 |
+
## Next Steps for Research Paper
|
| 70 |
+
You can now run the full benchmark:
|
| 71 |
+
|
| 72 |
+
```bash
|
| 73 |
+
# Full benchmark for research paper
|
| 74 |
+
python benchmark_schedule_performance.py \
|
| 75 |
+
--fleet-sizes 10 15 20 25 30 40 \
|
| 76 |
+
--methods ga pso cmaes sa \
|
| 77 |
+
--runs 5
|
| 78 |
+
```
|
| 79 |
+
|
| 80 |
+
This will generate:
|
| 81 |
+
- **JSON file**: Raw data with all metrics
|
| 82 |
+
- **Text report**: Formatted summary with performance rankings
|
| 83 |
+
- Data for your Results section on:
|
| 84 |
+
- Schedule generation time
|
| 85 |
+
- Computational efficiency comparisons
|
| 86 |
+
- Success rates
|
| 87 |
+
- Performance scaling with fleet size
|
DataService/enhanced_generator.py
CHANGED
|
@@ -256,7 +256,7 @@ class EnhancedMetroDataGenerator:
|
|
| 256 |
estimated_hours = random.randint(2, 24)
|
| 257 |
|
| 258 |
job = {
|
| 259 |
-
"
|
| 260 |
"trainset_id": ts_id,
|
| 261 |
"work_order_number": f"WO-{random.randint(100000, 999999)}",
|
| 262 |
"job_type": random.choice(job_types),
|
|
|
|
| 256 |
estimated_hours = random.randint(2, 24)
|
| 257 |
|
| 258 |
job = {
|
| 259 |
+
"job_id": f"JC-{random.randint(10000, 99999)}",
|
| 260 |
"trainset_id": ts_id,
|
| 261 |
"work_order_number": f"WO-{random.randint(100000, 999999)}",
|
| 262 |
"job_type": random.choice(job_types),
|
LICENSE
ADDED
|
File without changes
|
README.md
DELETED
|
@@ -1,191 +0,0 @@
|
|
| 1 |
-
# Metro Train Scheduling Service
|
| 2 |
-
|
| 3 |
-
This repository maintains two intelligent services that work together to optimize metro train scheduling:
|
| 4 |
-
|
| 5 |
-
## 1. Optimization Engine (DataService)
|
| 6 |
-
Traditional constraint-based optimization using OR-Tools for guaranteed valid schedules.
|
| 7 |
-
|
| 8 |
-
## 2. Self-Training ML Engine (SelfTrainService)
|
| 9 |
-
**Multi-Model Ensemble Learning** that continuously improves from real scheduling data.
|
| 10 |
-
|
| 11 |
-
### ML Models Included:
|
| 12 |
-
- **Gradient Boosting** (scikit-learn)
|
| 13 |
-
- **Random Forest** (scikit-learn)
|
| 14 |
-
- **XGBoost** - Extreme Gradient Boosting
|
| 15 |
-
- **LightGBM** - Microsoft's high-performance gradient boosting
|
| 16 |
-
- **CatBoost** - Yandex's categorical boosting
|
| 17 |
-
|
| 18 |
-
### Ensemble Strategy:
|
| 19 |
-
- Trains all 5 models simultaneously
|
| 20 |
-
- Uses weighted ensemble voting for predictions
|
| 21 |
-
- Weights based on individual model performance (R² score)
|
| 22 |
-
- Automatically selects best single model as fallback
|
| 23 |
-
- Higher prediction confidence when models agree
|
| 24 |
-
|
| 25 |
-
## General Flow
|
| 26 |
-
|
| 27 |
-
**Call a single endpoint** - the hybrid scheduler will internally decide:
|
| 28 |
-
|
| 29 |
-
1. **ML First**: Try ensemble ML prediction
|
| 30 |
-
- If confidence > 75% → Use ML-generated schedule
|
| 31 |
-
- Models vote weighted by performance
|
| 32 |
-
|
| 33 |
-
2. **Optimization Fallback**: If ML confidence low
|
| 34 |
-
- Falls back to traditional OR-Tools optimization
|
| 35 |
-
- Guaranteed valid schedule
|
| 36 |
-
|
| 37 |
-
3. **Continuous Learning**: Every 48 hours
|
| 38 |
-
- Automatically retrains all 5 models
|
| 39 |
-
- Uses accumulated real schedule data
|
| 40 |
-
- Updates ensemble weights
|
| 41 |
-
- Identifies new best model
|
| 42 |
-
|
| 43 |
-
## Key Features
|
| 44 |
-
|
| 45 |
-
✅ **Multi-Model Ensemble**: 5 state-of-the-art ML models working together
|
| 46 |
-
✅ **Auto-Retraining**: Retrains every 48 hours with new data
|
| 47 |
-
✅ **Confidence-Based**: Uses ML when confident, optimization as safety net
|
| 48 |
-
✅ **Performance Tracking**: Monitors each model's accuracy
|
| 49 |
-
✅ **Weighted Voting**: Better models have more influence
|
| 50 |
-
✅ **Best Model Selection**: Always knows which single model performs best
|
| 51 |
-
|
| 52 |
-
## Quick Start
|
| 53 |
-
|
| 54 |
-
### 1. Install Dependencies
|
| 55 |
-
```bash
|
| 56 |
-
pip install -r requirements.txt
|
| 57 |
-
```
|
| 58 |
-
|
| 59 |
-
### 2. Generate Initial Training Data
|
| 60 |
-
```bash
|
| 61 |
-
python SelfTrainService/train_model.py
|
| 62 |
-
```
|
| 63 |
-
|
| 64 |
-
### 3. Start Auto-Retraining Service
|
| 65 |
-
```bash
|
| 66 |
-
python SelfTrainService/start_retraining.py
|
| 67 |
-
```
|
| 68 |
-
|
| 69 |
-
### 4. Start API Service
|
| 70 |
-
```bash
|
| 71 |
-
cd DataService
|
| 72 |
-
python api.py
|
| 73 |
-
```
|
| 74 |
-
|
| 75 |
-
## Testing
|
| 76 |
-
|
| 77 |
-
### Test Ensemble System
|
| 78 |
-
```bash
|
| 79 |
-
python SelfTrainService/test_ensemble.py
|
| 80 |
-
```
|
| 81 |
-
|
| 82 |
-
### Test API Endpoints
|
| 83 |
-
```bash
|
| 84 |
-
python test_api.py
|
| 85 |
-
```
|
| 86 |
-
|
| 87 |
-
## Model Performance
|
| 88 |
-
|
| 89 |
-
After training, check model performance:
|
| 90 |
-
- **Training summary**: `models/training_summary.json`
|
| 91 |
-
- **Training history**: `models/training_history.json`
|
| 92 |
-
- **Ensemble weights**: Shows contribution of each model
|
| 93 |
-
|
| 94 |
-
Example output:
|
| 95 |
-
```json
|
| 96 |
-
{
|
| 97 |
-
"best_model": "xgboost",
|
| 98 |
-
"ensemble_weights": {
|
| 99 |
-
"gradient_boosting": 0.195,
|
| 100 |
-
"random_forest": 0.187,
|
| 101 |
-
"xgboost": 0.215,
|
| 102 |
-
"lightgbm": 0.208,
|
| 103 |
-
"catboost": 0.195
|
| 104 |
-
}
|
| 105 |
-
}
|
| 106 |
-
```
|
| 107 |
-
|
| 108 |
-
## Configuration
|
| 109 |
-
|
| 110 |
-
Edit `SelfTrainService/config.py`:
|
| 111 |
-
|
| 112 |
-
```python
|
| 113 |
-
RETRAIN_INTERVAL_HOURS = 48 # How often to retrain
|
| 114 |
-
MODEL_TYPES = [ # Which models to use
|
| 115 |
-
"gradient_boosting",
|
| 116 |
-
"random_forest",
|
| 117 |
-
"xgboost",
|
| 118 |
-
"lightgbm",
|
| 119 |
-
"catboost"
|
| 120 |
-
]
|
| 121 |
-
USE_ENSEMBLE = True # Enable ensemble voting
|
| 122 |
-
ML_CONFIDENCE_THRESHOLD = 0.75 # Min confidence to use ML
|
| 123 |
-
```
|
| 124 |
-
|
| 125 |
-
## Architecture
|
| 126 |
-
|
| 127 |
-
```
|
| 128 |
-
┌─────────────────┐
|
| 129 |
-
│ API Request │
|
| 130 |
-
└────────┬────────┘
|
| 131 |
-
│
|
| 132 |
-
▼
|
| 133 |
-
┌─────────────────────┐
|
| 134 |
-
│ Hybrid Scheduler │
|
| 135 |
-
└────────┬────────────┘
|
| 136 |
-
│
|
| 137 |
-
┌────┴────┐
|
| 138 |
-
│ │
|
| 139 |
-
▼ ▼
|
| 140 |
-
┌────────┐ ┌──────────────┐
|
| 141 |
-
│ ML │ │ Optimization │
|
| 142 |
-
│Ensemble│ │ (OR-Tools) │
|
| 143 |
-
└───┬────┘ └──────┬───────┘
|
| 144 |
-
│ │
|
| 145 |
-
│ >75% <75% │
|
| 146 |
-
│ confidence │
|
| 147 |
-
│ │
|
| 148 |
-
└──────┬───────┘
|
| 149 |
-
│
|
| 150 |
-
▼
|
| 151 |
-
┌────────────┐
|
| 152 |
-
│ Schedule │
|
| 153 |
-
└────────────┘
|
| 154 |
-
```
|
| 155 |
-
|
| 156 |
-
## Ensemble Advantages
|
| 157 |
-
|
| 158 |
-
1. **Robustness**: Multiple models reduce overfitting risk
|
| 159 |
-
2. **Accuracy**: Ensemble typically outperforms single models
|
| 160 |
-
3. **Confidence**: Agreement between models indicates reliability
|
| 161 |
-
4. **Adaptability**: Different models capture different patterns
|
| 162 |
-
5. **Fault Tolerance**: If one model fails, others continue
|
| 163 |
-
|
| 164 |
-
## Documentation
|
| 165 |
-
|
| 166 |
-
- **Implementation Details**: See `docs/integrate.md`
|
| 167 |
-
- **Multi-Objective Optimization**: See `multi_obj_optimize.md`
|
| 168 |
-
- **API Reference**: See `DataService/api.py` docstrings
|
| 169 |
-
|
| 170 |
-
## Project Structure
|
| 171 |
-
|
| 172 |
-
```
|
| 173 |
-
mlservice/
|
| 174 |
-
├── DataService/ # Optimization & API
|
| 175 |
-
│ ├── api.py # FastAPI service
|
| 176 |
-
│ ├── metro_models.py # Data models
|
| 177 |
-
│ ├── metro_data_generator.py
|
| 178 |
-
│ └── schedule_optimizer.py
|
| 179 |
-
├── SelfTrainService/ # ML ensemble
|
| 180 |
-
│ ├── config.py # Configuration
|
| 181 |
-
│ ├── trainer.py # Multi-model training
|
| 182 |
-
│ ├── data_store.py # Data persistence
|
| 183 |
-
│ ├── feature_extractor.py
|
| 184 |
-
│ ├── hybrid_scheduler.py
|
| 185 |
-
│ ├── retraining_service.py
|
| 186 |
-
│ ├── train_model.py # Manual training
|
| 187 |
-
│ ├── test_ensemble.py # Test suite
|
| 188 |
-
│ └── start_retraining.py
|
| 189 |
-
└── requirements.txt
|
| 190 |
-
```
|
| 191 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
api/greedyoptim_api.py
CHANGED
|
@@ -451,6 +451,13 @@ async def compare_methods(request: CompareMethodsRequest):
|
|
| 451 |
best_method = None
|
| 452 |
|
| 453 |
for method, result in results.items():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 454 |
comparison["methods"][method] = convert_result_to_response(
|
| 455 |
result, method
|
| 456 |
).dict()
|
|
@@ -460,7 +467,7 @@ async def compare_methods(request: CompareMethodsRequest):
|
|
| 460 |
best_method = method
|
| 461 |
|
| 462 |
comparison["summary"]["best_method"] = best_method
|
| 463 |
-
comparison["summary"]["best_score"] = best_score
|
| 464 |
|
| 465 |
logger.info(f"Comparison completed, best: {best_method} ({best_score:.4f})")
|
| 466 |
|
|
@@ -488,14 +495,21 @@ async def generate_synthetic_data(request: SyntheticDataRequest):
|
|
| 488 |
generator = EnhancedMetroDataGenerator(num_trainsets=request.num_trainsets)
|
| 489 |
data = generator.generate_complete_enhanced_dataset()
|
| 490 |
|
| 491 |
-
#
|
| 492 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 493 |
|
| 494 |
logger.info(f"Generated synthetic data with {len(data['trainset_status'])} trainsets")
|
| 495 |
|
| 496 |
return JSONResponse(content={
|
| 497 |
"success": True,
|
| 498 |
-
"data":
|
| 499 |
"metadata": {
|
| 500 |
"num_trainsets": len(data['trainset_status']),
|
| 501 |
"num_fitness_certificates": len(data['fitness_certificates']),
|
|
|
|
| 451 |
best_method = None
|
| 452 |
|
| 453 |
for method, result in results.items():
|
| 454 |
+
if result is None:
|
| 455 |
+
comparison["methods"][method] = {
|
| 456 |
+
"success": False,
|
| 457 |
+
"error": "Optimization failed for this method"
|
| 458 |
+
}
|
| 459 |
+
continue
|
| 460 |
+
|
| 461 |
comparison["methods"][method] = convert_result_to_response(
|
| 462 |
result, method
|
| 463 |
).dict()
|
|
|
|
| 467 |
best_method = method
|
| 468 |
|
| 469 |
comparison["summary"]["best_method"] = best_method
|
| 470 |
+
comparison["summary"]["best_score"] = best_score if best_method else None
|
| 471 |
|
| 472 |
logger.info(f"Comparison completed, best: {best_method} ({best_score:.4f})")
|
| 473 |
|
|
|
|
| 495 |
generator = EnhancedMetroDataGenerator(num_trainsets=request.num_trainsets)
|
| 496 |
data = generator.generate_complete_enhanced_dataset()
|
| 497 |
|
| 498 |
+
# Remove trainset_profiles as it contains non-serializable datetime objects
|
| 499 |
+
# and is not needed for optimization
|
| 500 |
+
data_for_response = {
|
| 501 |
+
"trainset_status": data["trainset_status"],
|
| 502 |
+
"fitness_certificates": data["fitness_certificates"],
|
| 503 |
+
"job_cards": data["job_cards"],
|
| 504 |
+
"component_health": data["component_health"],
|
| 505 |
+
"metadata": data.get("metadata", {})
|
| 506 |
+
}
|
| 507 |
|
| 508 |
logger.info(f"Generated synthetic data with {len(data['trainset_status'])} trainsets")
|
| 509 |
|
| 510 |
return JSONResponse(content={
|
| 511 |
"success": True,
|
| 512 |
+
"data": data_for_response,
|
| 513 |
"metadata": {
|
| 514 |
"num_trainsets": len(data['trainset_status']),
|
| 515 |
"num_fitness_certificates": len(data['fitness_certificates']),
|
api/test_greedyoptim_api.py
CHANGED
|
@@ -205,11 +205,11 @@ def test_custom_data():
|
|
| 205 |
print("Testing with Custom Minimal Data")
|
| 206 |
print("="*70)
|
| 207 |
|
| 208 |
-
# Create minimal valid data
|
| 209 |
custom_data = {
|
| 210 |
"trainset_status": [
|
| 211 |
-
{"trainset_id": f"KMRL-{i:02d}", "operational_status": "Available"}
|
| 212 |
-
for i in range(1,
|
| 213 |
],
|
| 214 |
"fitness_certificates": [
|
| 215 |
{
|
|
@@ -218,7 +218,7 @@ def test_custom_data():
|
|
| 218 |
"status": "Valid",
|
| 219 |
"expiry_date": (datetime.now() + timedelta(days=365)).isoformat()
|
| 220 |
}
|
| 221 |
-
for i in range(1,
|
| 222 |
],
|
| 223 |
"job_cards": [], # No job cards
|
| 224 |
"component_health": [
|
|
@@ -228,12 +228,12 @@ def test_custom_data():
|
|
| 228 |
"status": "Good",
|
| 229 |
"wear_level": 20.0
|
| 230 |
}
|
| 231 |
-
for i in range(1,
|
| 232 |
],
|
| 233 |
"method": "ga",
|
| 234 |
"config": {
|
| 235 |
-
"required_service_trains":
|
| 236 |
-
"min_standby":
|
| 237 |
"population_size": 20,
|
| 238 |
"generations": 30
|
| 239 |
}
|
|
|
|
| 205 |
print("Testing with Custom Minimal Data")
|
| 206 |
print("="*70)
|
| 207 |
|
| 208 |
+
# Create minimal valid data with at least 15 available trainsets
|
| 209 |
custom_data = {
|
| 210 |
"trainset_status": [
|
| 211 |
+
{"trainset_id": f"KMRL-{i:02d}", "operational_status": "Available", "total_mileage_km": 50000.0}
|
| 212 |
+
for i in range(1, 21)
|
| 213 |
],
|
| 214 |
"fitness_certificates": [
|
| 215 |
{
|
|
|
|
| 218 |
"status": "Valid",
|
| 219 |
"expiry_date": (datetime.now() + timedelta(days=365)).isoformat()
|
| 220 |
}
|
| 221 |
+
for i in range(1, 21)
|
| 222 |
],
|
| 223 |
"job_cards": [], # No job cards
|
| 224 |
"component_health": [
|
|
|
|
| 228 |
"status": "Good",
|
| 229 |
"wear_level": 20.0
|
| 230 |
}
|
| 231 |
+
for i in range(1, 21)
|
| 232 |
],
|
| 233 |
"method": "ga",
|
| 234 |
"config": {
|
| 235 |
+
"required_service_trains": 15,
|
| 236 |
+
"min_standby": 2,
|
| 237 |
"population_size": 20,
|
| 238 |
"generations": 30
|
| 239 |
}
|
benchmark_optimizers.py
ADDED
|
@@ -0,0 +1,431 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Benchmark script for comparing optimizer performance
|
| 4 |
+
Measures schedule generation time and computational efficiency
|
| 5 |
+
"""
|
| 6 |
+
import time
|
| 7 |
+
import json
|
| 8 |
+
import statistics
|
| 9 |
+
from datetime import datetime, date
|
| 10 |
+
from typing import Dict, List, Any, Optional
|
| 11 |
+
import sys
|
| 12 |
+
import os
|
| 13 |
+
|
| 14 |
+
# Add parent directory to path
|
| 15 |
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__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(),
|
| 32 |
+
"description": "Metro Schedule Optimization Performance Comparison"
|
| 33 |
+
},
|
| 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}%",
|
| 112 |
+
"execution_times": {
|
| 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%",
|
| 130 |
+
"error": "All runs failed"
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
print(f"\nSummary:")
|
| 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"""
|
| 179 |
+
print("\n" + "="*70)
|
| 180 |
+
print("COMPREHENSIVE OPTIMIZER BENCHMARK")
|
| 181 |
+
print("="*70)
|
| 182 |
+
print(f"Fleet Sizes to Test: {fleet_sizes}")
|
| 183 |
+
print(f"Runs per Configuration: {num_runs}")
|
| 184 |
+
print("="*70)
|
| 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
|
| 204 |
+
for fleet_size in fleet_sizes:
|
| 205 |
+
print(f"\n{'#'*70}")
|
| 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 |
+
)
|
| 216 |
+
self.results["results"].append(result)
|
| 217 |
+
|
| 218 |
+
# Small delay between tests
|
| 219 |
+
time.sleep(0.5)
|
| 220 |
+
|
| 221 |
+
# Generate comparison summary
|
| 222 |
+
self._generate_summary()
|
| 223 |
+
|
| 224 |
+
def _generate_summary(self):
|
| 225 |
+
"""Generate comparative summary of results"""
|
| 226 |
+
print("\n" + "="*70)
|
| 227 |
+
print("BENCHMARK SUMMARY")
|
| 228 |
+
print("="*70)
|
| 229 |
+
|
| 230 |
+
# Group by fleet size
|
| 231 |
+
fleet_sizes = sorted(set(r["fleet_size"] for r in self.results["results"]))
|
| 232 |
+
|
| 233 |
+
summary = {
|
| 234 |
+
"by_fleet_size": {},
|
| 235 |
+
"overall_rankings": {}
|
| 236 |
+
}
|
| 237 |
+
|
| 238 |
+
for fleet_size in fleet_sizes:
|
| 239 |
+
fleet_results = [r for r in self.results["results"] if r["fleet_size"] == fleet_size]
|
| 240 |
+
|
| 241 |
+
print(f"\nFleet Size: {fleet_size} trains")
|
| 242 |
+
print("-" * 70)
|
| 243 |
+
print(f"{'Optimizer':<25} {'Avg Time (s)':<15} {'Success Rate':<15}")
|
| 244 |
+
print("-" * 70)
|
| 245 |
+
|
| 246 |
+
fleet_summary = []
|
| 247 |
+
for result in fleet_results:
|
| 248 |
+
optimizer = result["optimizer"]
|
| 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()
|
benchmark_schedule_performance.py
ADDED
|
@@ -0,0 +1,498 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Comprehensive benchmark for schedule generation performance.
|
| 4 |
+
Tests MetroScheduleOptimizer and greedyOptim methods across different fleet sizes.
|
| 5 |
+
"""
|
| 6 |
+
import time
|
| 7 |
+
import statistics
|
| 8 |
+
import json
|
| 9 |
+
from datetime import datetime
|
| 10 |
+
from typing import Dict, List, Any, Optional
|
| 11 |
+
import sys
|
| 12 |
+
import argparse
|
| 13 |
+
|
| 14 |
+
# Import DataService components
|
| 15 |
+
from DataService.schedule_optimizer import MetroScheduleOptimizer
|
| 16 |
+
from DataService.metro_data_generator import MetroDataGenerator
|
| 17 |
+
from DataService.enhanced_generator import EnhancedMetroDataGenerator
|
| 18 |
+
|
| 19 |
+
# Import greedyOptim components
|
| 20 |
+
from greedyOptim.scheduler import optimize_trainset_schedule
|
| 21 |
+
from greedyOptim.models import OptimizationConfig
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class SchedulePerformanceBenchmark:
|
| 25 |
+
"""Benchmark schedule generation performance"""
|
| 26 |
+
|
| 27 |
+
def __init__(self):
|
| 28 |
+
self.results = {
|
| 29 |
+
"benchmark_info": {
|
| 30 |
+
"date": datetime.now().isoformat(),
|
| 31 |
+
"description": "Metro Schedule Generation Performance Analysis",
|
| 32 |
+
"test_type": "Schedule Generation Time & Computational Efficiency"
|
| 33 |
+
},
|
| 34 |
+
"configurations": [],
|
| 35 |
+
"detailed_results": [],
|
| 36 |
+
"summary": {}
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
def benchmark_schedule_generation(
|
| 40 |
+
self,
|
| 41 |
+
num_trains: int,
|
| 42 |
+
num_stations: int = 22,
|
| 43 |
+
num_runs: int = 3
|
| 44 |
+
) -> Dict[str, Any]:
|
| 45 |
+
"""Benchmark the MetroScheduleOptimizer"""
|
| 46 |
+
print(f"\n{'='*70}")
|
| 47 |
+
print(f"Benchmarking Schedule Generation")
|
| 48 |
+
print(f"Fleet Size: {num_trains} trains | Stations: {num_stations}")
|
| 49 |
+
print(f"{'='*70}")
|
| 50 |
+
|
| 51 |
+
run_times = []
|
| 52 |
+
success_count = 0
|
| 53 |
+
schedule_stats = []
|
| 54 |
+
|
| 55 |
+
for run in range(num_runs):
|
| 56 |
+
print(f"\nRun {run + 1}/{num_runs}...", end=" ")
|
| 57 |
+
|
| 58 |
+
try:
|
| 59 |
+
# Generate data
|
| 60 |
+
generator = MetroDataGenerator(num_trains=num_trains)
|
| 61 |
+
|
| 62 |
+
route = generator.generate_route(route_name="Aluva-Pettah Line")
|
| 63 |
+
|
| 64 |
+
train_health = generator.generate_train_health_statuses()
|
| 65 |
+
|
| 66 |
+
# Time the schedule generation
|
| 67 |
+
start_time = time.perf_counter()
|
| 68 |
+
|
| 69 |
+
optimizer = MetroScheduleOptimizer(
|
| 70 |
+
date="2025-11-06",
|
| 71 |
+
num_trains=num_trains,
|
| 72 |
+
route=route,
|
| 73 |
+
train_health=train_health
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
schedule = optimizer.optimize_schedule()
|
| 77 |
+
|
| 78 |
+
end_time = time.perf_counter()
|
| 79 |
+
elapsed = end_time - start_time
|
| 80 |
+
|
| 81 |
+
run_times.append(elapsed)
|
| 82 |
+
success_count += 1
|
| 83 |
+
|
| 84 |
+
# Collect schedule statistics
|
| 85 |
+
stats = {
|
| 86 |
+
"num_trainsets": len(schedule.trainsets),
|
| 87 |
+
"num_in_service": len([t for t in schedule.trainsets if t.status == "IN_SERVICE"]),
|
| 88 |
+
"num_standby": len([t for t in schedule.trainsets if t.status == "STANDBY"]),
|
| 89 |
+
"num_maintenance": len([t for t in schedule.trainsets if t.status == "UNDER_MAINTENANCE"]),
|
| 90 |
+
"total_service_blocks": sum(len(t.service_blocks) for t in schedule.trainsets),
|
| 91 |
+
}
|
| 92 |
+
schedule_stats.append(stats)
|
| 93 |
+
|
| 94 |
+
print(f"✓ {elapsed:.4f}s | In Service: {stats['num_in_service']}/{stats['num_trainsets']}")
|
| 95 |
+
|
| 96 |
+
except Exception as e:
|
| 97 |
+
print(f"✗ Failed: {str(e)[:60]}")
|
| 98 |
+
continue
|
| 99 |
+
|
| 100 |
+
# Calculate statistics
|
| 101 |
+
if run_times:
|
| 102 |
+
avg_stats = {}
|
| 103 |
+
if schedule_stats:
|
| 104 |
+
for key in schedule_stats[0].keys():
|
| 105 |
+
values = [s[key] for s in schedule_stats]
|
| 106 |
+
avg_stats[key] = {
|
| 107 |
+
"mean": statistics.mean(values),
|
| 108 |
+
"min": min(values),
|
| 109 |
+
"max": max(values)
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
result = {
|
| 113 |
+
"optimizer": "MetroScheduleOptimizer",
|
| 114 |
+
"fleet_size": num_trains,
|
| 115 |
+
"num_stations": num_stations,
|
| 116 |
+
"num_runs": num_runs,
|
| 117 |
+
"successful_runs": success_count,
|
| 118 |
+
"success_rate": f"{(success_count/num_runs)*100:.1f}%",
|
| 119 |
+
"execution_time": {
|
| 120 |
+
"min_seconds": min(run_times),
|
| 121 |
+
"max_seconds": max(run_times),
|
| 122 |
+
"mean_seconds": statistics.mean(run_times),
|
| 123 |
+
"median_seconds": statistics.median(run_times),
|
| 124 |
+
"stdev_seconds": statistics.stdev(run_times) if len(run_times) > 1 else 0,
|
| 125 |
+
"all_runs_seconds": run_times
|
| 126 |
+
},
|
| 127 |
+
"schedule_statistics": avg_stats
|
| 128 |
+
}
|
| 129 |
+
else:
|
| 130 |
+
result = {
|
| 131 |
+
"optimizer": "MetroScheduleOptimizer",
|
| 132 |
+
"fleet_size": num_trains,
|
| 133 |
+
"num_stations": num_stations,
|
| 134 |
+
"num_runs": num_runs,
|
| 135 |
+
"successful_runs": 0,
|
| 136 |
+
"success_rate": "0%",
|
| 137 |
+
"error": "All runs failed"
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
print(f"\nSummary:")
|
| 141 |
+
print(f" Success Rate: {result['success_rate']}")
|
| 142 |
+
if run_times:
|
| 143 |
+
print(f" Mean Time: {result['execution_time']['mean_seconds']:.4f}s")
|
| 144 |
+
print(f" Std Dev: {result['execution_time']['stdev_seconds']:.4f}s")
|
| 145 |
+
|
| 146 |
+
return result
|
| 147 |
+
|
| 148 |
+
def benchmark_greedy_optimizers(
|
| 149 |
+
self,
|
| 150 |
+
num_trains: int,
|
| 151 |
+
methods: List[str] = ['ga', 'cmaes', 'pso'],
|
| 152 |
+
num_runs: int = 3
|
| 153 |
+
) -> Dict[str, List[Dict[str, Any]]]:
|
| 154 |
+
"""Benchmark greedyOptim methods"""
|
| 155 |
+
print(f"\n{'='*70}")
|
| 156 |
+
print(f"Benchmarking Greedy Optimization Methods")
|
| 157 |
+
print(f"Fleet Size: {num_trains} trains | Methods: {methods}")
|
| 158 |
+
print(f"{'='*70}")
|
| 159 |
+
|
| 160 |
+
results_by_method = {}
|
| 161 |
+
|
| 162 |
+
# Generate complete synthetic data using EnhancedMetroDataGenerator
|
| 163 |
+
print(f"Generating complete synthetic data for {num_trains} trains...")
|
| 164 |
+
try:
|
| 165 |
+
# Use EnhancedMetroDataGenerator for complete, realistic data
|
| 166 |
+
generator = EnhancedMetroDataGenerator(num_trainsets=num_trains)
|
| 167 |
+
synthetic_data = generator.generate_complete_enhanced_dataset()
|
| 168 |
+
|
| 169 |
+
print(f" ✓ Generated {len(synthetic_data['trainset_status'])} trainset statuses")
|
| 170 |
+
print(f" ✓ Generated {len(synthetic_data['fitness_certificates'])} fitness certificates")
|
| 171 |
+
print(f" ✓ Generated {len(synthetic_data['job_cards'])} job cards")
|
| 172 |
+
|
| 173 |
+
except Exception as e:
|
| 174 |
+
print(f"✗ Failed to generate synthetic data: {e}")
|
| 175 |
+
import traceback
|
| 176 |
+
traceback.print_exc()
|
| 177 |
+
return results_by_method
|
| 178 |
+
|
| 179 |
+
for method in methods:
|
| 180 |
+
print(f"\n--- Testing Method: {method.upper()} ---")
|
| 181 |
+
|
| 182 |
+
run_times = []
|
| 183 |
+
success_count = 0
|
| 184 |
+
results = []
|
| 185 |
+
|
| 186 |
+
for run in range(num_runs):
|
| 187 |
+
print(f"Run {run + 1}/{num_runs}...", end=" ")
|
| 188 |
+
|
| 189 |
+
try:
|
| 190 |
+
config = OptimizationConfig()
|
| 191 |
+
|
| 192 |
+
start_time = time.perf_counter()
|
| 193 |
+
result = optimize_trainset_schedule(synthetic_data, method, config)
|
| 194 |
+
end_time = time.perf_counter()
|
| 195 |
+
|
| 196 |
+
elapsed = end_time - start_time
|
| 197 |
+
run_times.append(elapsed)
|
| 198 |
+
success_count += 1
|
| 199 |
+
results.append(result)
|
| 200 |
+
|
| 201 |
+
print(f"✓ {elapsed:.4f}s | Score: {result.fitness_score:.4f}")
|
| 202 |
+
|
| 203 |
+
except Exception as e:
|
| 204 |
+
print(f"✗ Failed: {str(e)[:50]}")
|
| 205 |
+
continue
|
| 206 |
+
|
| 207 |
+
if run_times:
|
| 208 |
+
method_result = {
|
| 209 |
+
"method": method,
|
| 210 |
+
"optimizer_family": "GreedyOptim",
|
| 211 |
+
"fleet_size": num_trains,
|
| 212 |
+
"num_runs": num_runs,
|
| 213 |
+
"successful_runs": success_count,
|
| 214 |
+
"success_rate": f"{(success_count/num_runs)*100:.1f}%",
|
| 215 |
+
"execution_time": {
|
| 216 |
+
"min_seconds": min(run_times),
|
| 217 |
+
"max_seconds": max(run_times),
|
| 218 |
+
"mean_seconds": statistics.mean(run_times),
|
| 219 |
+
"median_seconds": statistics.median(run_times),
|
| 220 |
+
"stdev_seconds": statistics.stdev(run_times) if len(run_times) > 1 else 0,
|
| 221 |
+
},
|
| 222 |
+
"optimization_scores": {
|
| 223 |
+
"mean": statistics.mean([r.fitness_score for r in results]),
|
| 224 |
+
"min": min([r.fitness_score for r in results]),
|
| 225 |
+
"max": max([r.fitness_score for r in results]),
|
| 226 |
+
}
|
| 227 |
+
}
|
| 228 |
+
results_by_method[method] = method_result
|
| 229 |
+
|
| 230 |
+
return results_by_method
|
| 231 |
+
|
| 232 |
+
def run_comprehensive_benchmark(
|
| 233 |
+
self,
|
| 234 |
+
fleet_sizes: List[int] = [10, 20, 30],
|
| 235 |
+
greedy_methods: List[str] = ['ga', 'cmaes', 'pso'],
|
| 236 |
+
num_runs: int = 3
|
| 237 |
+
):
|
| 238 |
+
"""Run comprehensive performance benchmark"""
|
| 239 |
+
print("\n" + "="*70)
|
| 240 |
+
print("COMPREHENSIVE SCHEDULE GENERATION PERFORMANCE BENCHMARK")
|
| 241 |
+
print("="*70)
|
| 242 |
+
print(f"Fleet Sizes: {fleet_sizes}")
|
| 243 |
+
print(f"Greedy Methods: {greedy_methods}")
|
| 244 |
+
print(f"Runs per Configuration: {num_runs}")
|
| 245 |
+
print("="*70)
|
| 246 |
+
|
| 247 |
+
# Store configurations
|
| 248 |
+
self.results["configurations"].append({
|
| 249 |
+
"fleet_sizes": fleet_sizes,
|
| 250 |
+
"greedy_methods": greedy_methods,
|
| 251 |
+
"runs_per_config": num_runs,
|
| 252 |
+
"station_count": 22
|
| 253 |
+
})
|
| 254 |
+
|
| 255 |
+
all_results = []
|
| 256 |
+
|
| 257 |
+
for fleet_size in fleet_sizes:
|
| 258 |
+
print(f"\n{'#'*70}")
|
| 259 |
+
print(f"# FLEET SIZE: {fleet_size} TRAINS")
|
| 260 |
+
print(f"{'#'*70}")
|
| 261 |
+
|
| 262 |
+
# Benchmark Schedule Generation
|
| 263 |
+
schedule_result = self.benchmark_schedule_generation(
|
| 264 |
+
num_trains=fleet_size,
|
| 265 |
+
num_runs=num_runs
|
| 266 |
+
)
|
| 267 |
+
all_results.append(schedule_result)
|
| 268 |
+
|
| 269 |
+
# Benchmark Greedy Optimizers
|
| 270 |
+
greedy_results = self.benchmark_greedy_optimizers(
|
| 271 |
+
num_trains=fleet_size,
|
| 272 |
+
methods=greedy_methods,
|
| 273 |
+
num_runs=num_runs
|
| 274 |
+
)
|
| 275 |
+
|
| 276 |
+
for method, result in greedy_results.items():
|
| 277 |
+
all_results.append(result)
|
| 278 |
+
|
| 279 |
+
time.sleep(0.5) # Brief pause between fleet sizes
|
| 280 |
+
|
| 281 |
+
self.results["detailed_results"] = all_results
|
| 282 |
+
self._generate_performance_summary()
|
| 283 |
+
|
| 284 |
+
def _generate_performance_summary(self):
|
| 285 |
+
"""Generate comparative performance summary"""
|
| 286 |
+
print("\n" + "="*70)
|
| 287 |
+
print("PERFORMANCE SUMMARY")
|
| 288 |
+
print("="*70)
|
| 289 |
+
|
| 290 |
+
# Group by fleet size
|
| 291 |
+
fleet_sizes = sorted(set(
|
| 292 |
+
r["fleet_size"] for r in self.results["detailed_results"]
|
| 293 |
+
if "fleet_size" in r
|
| 294 |
+
))
|
| 295 |
+
|
| 296 |
+
summary_by_fleet = {}
|
| 297 |
+
|
| 298 |
+
for fleet_size in fleet_sizes:
|
| 299 |
+
fleet_results = [
|
| 300 |
+
r for r in self.results["detailed_results"]
|
| 301 |
+
if r.get("fleet_size") == fleet_size and "execution_time" in r
|
| 302 |
+
]
|
| 303 |
+
|
| 304 |
+
print(f"\n{'Fleet Size:':<20} {fleet_size} trains")
|
| 305 |
+
print("-" * 70)
|
| 306 |
+
print(f"{'Optimizer':<30} {'Mean Time (s)':<15} {'Success Rate':<15}")
|
| 307 |
+
print("-" * 70)
|
| 308 |
+
|
| 309 |
+
fleet_summary = []
|
| 310 |
+
for result in fleet_results:
|
| 311 |
+
name = result.get("optimizer") or result.get("method", "Unknown")
|
| 312 |
+
mean_time = result["execution_time"]["mean_seconds"]
|
| 313 |
+
success = result["success_rate"]
|
| 314 |
+
|
| 315 |
+
print(f"{name:<30} {mean_time:<15.4f} {success:<15}")
|
| 316 |
+
fleet_summary.append({
|
| 317 |
+
"optimizer": name,
|
| 318 |
+
"mean_time_seconds": mean_time,
|
| 319 |
+
"success_rate": success
|
| 320 |
+
})
|
| 321 |
+
|
| 322 |
+
summary_by_fleet[fleet_size] = fleet_summary
|
| 323 |
+
|
| 324 |
+
# Overall rankings
|
| 325 |
+
print("\n" + "="*70)
|
| 326 |
+
print("OVERALL PERFORMANCE RANKINGS (by average time)")
|
| 327 |
+
print("="*70)
|
| 328 |
+
|
| 329 |
+
optimizer_times = {}
|
| 330 |
+
for result in self.results["detailed_results"]:
|
| 331 |
+
if "execution_time" not in result:
|
| 332 |
+
continue
|
| 333 |
+
|
| 334 |
+
name = result.get("optimizer") or result.get("method", "Unknown")
|
| 335 |
+
if name not in optimizer_times:
|
| 336 |
+
optimizer_times[name] = []
|
| 337 |
+
optimizer_times[name].append(result["execution_time"]["mean_seconds"])
|
| 338 |
+
|
| 339 |
+
rankings = [
|
| 340 |
+
(name, statistics.mean(times))
|
| 341 |
+
for name, times in optimizer_times.items()
|
| 342 |
+
]
|
| 343 |
+
rankings.sort(key=lambda x: x[1])
|
| 344 |
+
|
| 345 |
+
print(f"\n{'Rank':<8} {'Optimizer/Method':<30} {'Avg Time (s)':<15}")
|
| 346 |
+
print("-" * 70)
|
| 347 |
+
for rank, (name, avg_time) in enumerate(rankings, 1):
|
| 348 |
+
print(f"{rank:<8} {name:<30} {avg_time:<15.4f}")
|
| 349 |
+
|
| 350 |
+
self.results["summary"] = {
|
| 351 |
+
"by_fleet_size": summary_by_fleet,
|
| 352 |
+
"overall_rankings": {
|
| 353 |
+
name: {"rank": rank, "avg_time_seconds": avg_time}
|
| 354 |
+
for rank, (name, avg_time) in enumerate(rankings, 1)
|
| 355 |
+
},
|
| 356 |
+
"fastest_optimizer": rankings[0][0] if rankings else None,
|
| 357 |
+
"fastest_time_seconds": rankings[0][1] if rankings else None
|
| 358 |
+
}
|
| 359 |
+
|
| 360 |
+
def save_results(self, filename: Optional[str] = None):
|
| 361 |
+
"""Save benchmark results to JSON file"""
|
| 362 |
+
if filename is None:
|
| 363 |
+
filename = f"schedule_benchmark_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
|
| 364 |
+
|
| 365 |
+
with open(filename, 'w') as f:
|
| 366 |
+
json.dump(self.results, f, indent=2, default=str)
|
| 367 |
+
|
| 368 |
+
print(f"\n{'='*70}")
|
| 369 |
+
print(f"Results saved to: {filename}")
|
| 370 |
+
print(f"{'='*70}")
|
| 371 |
+
|
| 372 |
+
return filename
|
| 373 |
+
|
| 374 |
+
def generate_report(self):
|
| 375 |
+
"""Generate formatted text report"""
|
| 376 |
+
report_filename = f"schedule_performance_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
|
| 377 |
+
|
| 378 |
+
with open(report_filename, 'w') as f:
|
| 379 |
+
f.write("="*80 + "\n")
|
| 380 |
+
f.write("METRO SCHEDULE GENERATION PERFORMANCE REPORT\n")
|
| 381 |
+
f.write("="*80 + "\n\n")
|
| 382 |
+
f.write(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
|
| 383 |
+
f.write(f"Test Type: Schedule Generation Time & Computational Efficiency\n\n")
|
| 384 |
+
|
| 385 |
+
# Executive Summary
|
| 386 |
+
f.write("EXECUTIVE SUMMARY\n")
|
| 387 |
+
f.write("-"*80 + "\n\n")
|
| 388 |
+
|
| 389 |
+
if "summary" in self.results and "fastest_optimizer" in self.results["summary"]:
|
| 390 |
+
f.write(f"Fastest Optimizer: {self.results['summary']['fastest_optimizer']}\n")
|
| 391 |
+
f.write(f"Best Average Time: {self.results['summary']['fastest_time_seconds']:.4f} seconds\n\n")
|
| 392 |
+
|
| 393 |
+
# Rankings
|
| 394 |
+
if "summary" in self.results and "overall_rankings" in self.results["summary"]:
|
| 395 |
+
f.write("Overall Performance Rankings:\n")
|
| 396 |
+
for name, data in sorted(
|
| 397 |
+
self.results["summary"]["overall_rankings"].items(),
|
| 398 |
+
key=lambda x: x[1]["rank"]
|
| 399 |
+
):
|
| 400 |
+
f.write(f" {data['rank']}. {name}: {data['avg_time_seconds']:.4f}s\n")
|
| 401 |
+
f.write("\n")
|
| 402 |
+
|
| 403 |
+
# Detailed Results
|
| 404 |
+
f.write("\nDETAILED RESULTS\n")
|
| 405 |
+
f.write("-"*80 + "\n\n")
|
| 406 |
+
|
| 407 |
+
for result in self.results["detailed_results"]:
|
| 408 |
+
name = result.get("optimizer") or result.get("method", "Unknown")
|
| 409 |
+
f.write(f"Optimizer/Method: {name}\n")
|
| 410 |
+
f.write(f"Fleet Size: {result.get('fleet_size', 'N/A')} trains\n")
|
| 411 |
+
f.write(f"Success Rate: {result.get('success_rate', 'N/A')}\n")
|
| 412 |
+
|
| 413 |
+
if "execution_time" in result:
|
| 414 |
+
f.write(f"Execution Time Statistics:\n")
|
| 415 |
+
f.write(f" Mean: {result['execution_time']['mean_seconds']:.4f}s\n")
|
| 416 |
+
f.write(f" Median: {result['execution_time']['median_seconds']:.4f}s\n")
|
| 417 |
+
f.write(f" Min: {result['execution_time']['min_seconds']:.4f}s\n")
|
| 418 |
+
f.write(f" Max: {result['execution_time']['max_seconds']:.4f}s\n")
|
| 419 |
+
f.write(f" StdDev: {result['execution_time']['stdev_seconds']:.4f}s\n")
|
| 420 |
+
|
| 421 |
+
if "optimization_scores" in result:
|
| 422 |
+
f.write(f"Optimization Scores:\n")
|
| 423 |
+
f.write(f" Mean: {result['optimization_scores']['mean']:.4f}\n")
|
| 424 |
+
f.write(f" Min: {result['optimization_scores']['min']:.4f}\n")
|
| 425 |
+
f.write(f" Max: {result['optimization_scores']['max']:.4f}\n")
|
| 426 |
+
|
| 427 |
+
f.write("\n" + "-"*80 + "\n\n")
|
| 428 |
+
|
| 429 |
+
print(f"Performance report saved to: {report_filename}")
|
| 430 |
+
return report_filename
|
| 431 |
+
|
| 432 |
+
|
| 433 |
+
def main():
|
| 434 |
+
"""Main execution function"""
|
| 435 |
+
import argparse
|
| 436 |
+
|
| 437 |
+
parser = argparse.ArgumentParser(
|
| 438 |
+
description="Benchmark metro schedule generation performance"
|
| 439 |
+
)
|
| 440 |
+
parser.add_argument(
|
| 441 |
+
"--fleet-sizes",
|
| 442 |
+
nargs="+",
|
| 443 |
+
type=int,
|
| 444 |
+
default=[10, 20, 30],
|
| 445 |
+
help="Fleet sizes to test (default: 10 20 30)"
|
| 446 |
+
)
|
| 447 |
+
parser.add_argument(
|
| 448 |
+
"--methods",
|
| 449 |
+
nargs="+",
|
| 450 |
+
default=['ga', 'cmaes', 'pso'],
|
| 451 |
+
help="Greedy optimization methods to test (default: ga cmaes pso)"
|
| 452 |
+
)
|
| 453 |
+
parser.add_argument(
|
| 454 |
+
"--runs",
|
| 455 |
+
type=int,
|
| 456 |
+
default=3,
|
| 457 |
+
help="Number of runs per configuration (default: 3)"
|
| 458 |
+
)
|
| 459 |
+
parser.add_argument(
|
| 460 |
+
"--quick",
|
| 461 |
+
action="store_true",
|
| 462 |
+
help="Quick test with minimal configurations"
|
| 463 |
+
)
|
| 464 |
+
|
| 465 |
+
args = parser.parse_args()
|
| 466 |
+
|
| 467 |
+
if args.quick:
|
| 468 |
+
fleet_sizes = [10, 20]
|
| 469 |
+
methods = ['ga']
|
| 470 |
+
num_runs = 2
|
| 471 |
+
print("\n*** QUICK BENCHMARK MODE ***")
|
| 472 |
+
else:
|
| 473 |
+
fleet_sizes = args.fleet_sizes
|
| 474 |
+
methods = args.methods
|
| 475 |
+
num_runs = args.runs
|
| 476 |
+
|
| 477 |
+
# Run benchmark
|
| 478 |
+
benchmark = SchedulePerformanceBenchmark()
|
| 479 |
+
benchmark.run_comprehensive_benchmark(
|
| 480 |
+
fleet_sizes=fleet_sizes,
|
| 481 |
+
greedy_methods=methods,
|
| 482 |
+
num_runs=num_runs
|
| 483 |
+
)
|
| 484 |
+
|
| 485 |
+
# Save results
|
| 486 |
+
json_file = benchmark.save_results()
|
| 487 |
+
report_file = benchmark.generate_report()
|
| 488 |
+
|
| 489 |
+
print("\n" + "="*70)
|
| 490 |
+
print("BENCHMARK COMPLETE")
|
| 491 |
+
print("="*70)
|
| 492 |
+
print(f"JSON Results: {json_file}")
|
| 493 |
+
print(f"Text Report: {report_file}")
|
| 494 |
+
print("="*70 + "\n")
|
| 495 |
+
|
| 496 |
+
|
| 497 |
+
if __name__ == "__main__":
|
| 498 |
+
main()
|
benchmarks/__init__.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Benchmarking Suite for Metro Train Scheduling System
|
| 3 |
+
|
| 4 |
+
This package contains comprehensive benchmarking tools for:
|
| 5 |
+
- Schedule generation performance
|
| 6 |
+
- Fleet utilization analysis
|
| 7 |
+
- Service quality metrics
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
__version__ = '1.0.0'
|
benchmarks/fleet_utilization/README.md
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Fleet Utilization Benchmarks
|
| 2 |
+
|
| 3 |
+
This directory contains tools for analyzing fleet utilization metrics for the metro train scheduling system.
|
| 4 |
+
|
| 5 |
+
## Overview
|
| 6 |
+
|
| 7 |
+
Fleet utilization analysis provides critical data for the **Results** section of your research paper, specifically:
|
| 8 |
+
|
| 9 |
+
1. **Minimum Fleet Size**: Calculate the minimum number of trains required to maintain service frequency
|
| 10 |
+
2. **Coverage Efficiency**: Measure percentage of peak vs. off-peak demand covered
|
| 11 |
+
3. **Train Utilization Rate**: Analyze average operational hours per train vs. idle time
|
| 12 |
+
|
| 13 |
+
## Components
|
| 14 |
+
|
| 15 |
+
### 1. `fleet_analyzer.py`
|
| 16 |
+
Core analysis module with the `FleetUtilizationAnalyzer` class.
|
| 17 |
+
|
| 18 |
+
**Key Features:**
|
| 19 |
+
- Calculate minimum fleet size based on headway requirements
|
| 20 |
+
- Analyze demand coverage (peak vs. off-peak)
|
| 21 |
+
- Compute train utilization rates
|
| 22 |
+
- Generate efficiency scores
|
| 23 |
+
- Find optimal fleet configurations
|
| 24 |
+
|
| 25 |
+
**Key Classes:**
|
| 26 |
+
- `FleetUtilizationAnalyzer`: Main analysis engine
|
| 27 |
+
- `FleetUtilizationMetrics`: Data class for results
|
| 28 |
+
|
| 29 |
+
### 2. `benchmark_fleet_utilization.py`
|
| 30 |
+
Comprehensive benchmarking script for research paper data collection.
|
| 31 |
+
|
| 32 |
+
**Features:**
|
| 33 |
+
- Test multiple fleet sizes
|
| 34 |
+
- Comparative analysis
|
| 35 |
+
- Statistical summaries
|
| 36 |
+
- JSON and text report generation
|
| 37 |
+
|
| 38 |
+
## Usage
|
| 39 |
+
|
| 40 |
+
### Quick Start
|
| 41 |
+
|
| 42 |
+
```bash
|
| 43 |
+
# Run comprehensive benchmark
|
| 44 |
+
python benchmark_fleet_utilization.py
|
| 45 |
+
```
|
| 46 |
+
|
| 47 |
+
This will:
|
| 48 |
+
- Analyze fleet sizes from 10-40 trains
|
| 49 |
+
- Calculate minimum requirements
|
| 50 |
+
- Measure coverage efficiency
|
| 51 |
+
- Compute utilization rates
|
| 52 |
+
- Generate JSON data and text report
|
| 53 |
+
|
| 54 |
+
### Custom Analysis
|
| 55 |
+
|
| 56 |
+
```python
|
| 57 |
+
from fleet_analyzer import FleetUtilizationAnalyzer
|
| 58 |
+
|
| 59 |
+
analyzer = FleetUtilizationAnalyzer()
|
| 60 |
+
|
| 61 |
+
# Analyze specific fleet size
|
| 62 |
+
metrics = analyzer.analyze_fleet_configuration(
|
| 63 |
+
total_fleet=25,
|
| 64 |
+
trains_in_maintenance=2
|
| 65 |
+
)
|
| 66 |
+
|
| 67 |
+
print(f"Coverage: {metrics.overall_coverage_percent:.1f}%")
|
| 68 |
+
print(f"Utilization: {metrics.utilization_rate_percent:.1f}%")
|
| 69 |
+
|
| 70 |
+
# Find optimal fleet size
|
| 71 |
+
optimal_size, optimal_metrics = analyzer.find_optimal_fleet_size(
|
| 72 |
+
min_coverage_required=95.0
|
| 73 |
+
)
|
| 74 |
+
print(f"Optimal Fleet: {optimal_size} trains")
|
| 75 |
+
```
|
| 76 |
+
|
| 77 |
+
### Programmatic Benchmark
|
| 78 |
+
|
| 79 |
+
```python
|
| 80 |
+
from benchmark_fleet_utilization import FleetUtilizationBenchmark
|
| 81 |
+
|
| 82 |
+
benchmark = FleetUtilizationBenchmark()
|
| 83 |
+
|
| 84 |
+
# Run custom analysis
|
| 85 |
+
benchmark.run_comprehensive_analysis(
|
| 86 |
+
fleet_sizes=[15, 20, 25, 30, 35],
|
| 87 |
+
maintenance_rate=0.1
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
# Save results
|
| 91 |
+
benchmark.save_results("my_results.json")
|
| 92 |
+
benchmark.generate_report("my_report.txt")
|
| 93 |
+
```
|
| 94 |
+
|
| 95 |
+
## Kochi Metro Configuration
|
| 96 |
+
|
| 97 |
+
The analyzer uses real Kochi Metro parameters:
|
| 98 |
+
|
| 99 |
+
- **Route Length**: 25.612 km
|
| 100 |
+
- **Average Speed**: 35 km/h (operating speed)
|
| 101 |
+
- **Service Hours**: 5:00 AM - 11:00 PM (18 hours)
|
| 102 |
+
- **Peak Hours**:
|
| 103 |
+
- Morning: 7:00-10:00 AM
|
| 104 |
+
- Evening: 5:00-8:00 PM
|
| 105 |
+
- **Target Headways**:
|
| 106 |
+
- Peak: 5 minutes
|
| 107 |
+
- Off-Peak: 10 minutes
|
| 108 |
+
- **Turnaround Time**: 10 minutes
|
| 109 |
+
|
| 110 |
+
## Output Files
|
| 111 |
+
|
| 112 |
+
### JSON Results (`fleet_utilization_benchmark_*.json`)
|
| 113 |
+
Complete data structure with:
|
| 114 |
+
- Metadata and configuration
|
| 115 |
+
- Detailed metrics for each fleet size
|
| 116 |
+
- Comparative statistics
|
| 117 |
+
- Optimal fleet configuration
|
| 118 |
+
|
| 119 |
+
### Text Report (`fleet_utilization_report_*.txt`)
|
| 120 |
+
Human-readable report with:
|
| 121 |
+
- Executive summary
|
| 122 |
+
- Optimal fleet configuration
|
| 123 |
+
- Comparative analysis
|
| 124 |
+
- Detailed results for each fleet size
|
| 125 |
+
|
| 126 |
+
## Metrics Explained
|
| 127 |
+
|
| 128 |
+
### 1. Minimum Fleet Size
|
| 129 |
+
**Formula**: `(Round Trip Time / Headway) + Buffer + Maintenance Reserve`
|
| 130 |
+
|
| 131 |
+
- Accounts for route travel time
|
| 132 |
+
- Includes operational buffers
|
| 133 |
+
- Considers maintenance requirements
|
| 134 |
+
|
| 135 |
+
**Research Paper Usage:**
|
| 136 |
+
> "Analysis revealed that a minimum fleet of 18 trains is required to maintain the target 5-minute headway during peak hours on the 25.612 km route."
|
| 137 |
+
|
| 138 |
+
### 2. Coverage Efficiency
|
| 139 |
+
|
| 140 |
+
**Metrics:**
|
| 141 |
+
- Peak demand coverage (%)
|
| 142 |
+
- Off-peak demand coverage (%)
|
| 143 |
+
- Overall weighted coverage (%)
|
| 144 |
+
|
| 145 |
+
**Research Paper Usage:**
|
| 146 |
+
> "A fleet of 25 trains achieved 100% peak demand coverage and 98.5% overall coverage across the 18-hour service period."
|
| 147 |
+
|
| 148 |
+
### 3. Train Utilization Rate
|
| 149 |
+
|
| 150 |
+
**Metrics:**
|
| 151 |
+
- Average operational hours per train
|
| 152 |
+
- Average idle hours per train
|
| 153 |
+
- Utilization rate percentage
|
| 154 |
+
|
| 155 |
+
**Formula**: `Utilization % = (Operational Hours / 24) × 100`
|
| 156 |
+
|
| 157 |
+
**Research Paper Usage:**
|
| 158 |
+
> "Fleet utilization analysis demonstrated an average of 16.2 operational hours per train (67.5% utilization rate), with 7.8 hours of scheduled idle time for charging and maintenance."
|
| 159 |
+
|
| 160 |
+
## Example Results
|
| 161 |
+
|
| 162 |
+
### Minimum Fleet Size Analysis
|
| 163 |
+
```
|
| 164 |
+
Fleet Size: 25 trains
|
| 165 |
+
Minimum Required: 18 trains
|
| 166 |
+
Excess Capacity: 7 trains (38.9%)
|
| 167 |
+
|
| 168 |
+
Peak Service: 15 trains
|
| 169 |
+
Off-Peak Service: 8 trains
|
| 170 |
+
Standby: 3 trains
|
| 171 |
+
Maintenance: 2 trains
|
| 172 |
+
```
|
| 173 |
+
|
| 174 |
+
### Coverage Efficiency
|
| 175 |
+
```
|
| 176 |
+
Peak Demand Coverage: 100.0%
|
| 177 |
+
Off-Peak Demand Coverage: 100.0%
|
| 178 |
+
Overall Coverage: 100.0%
|
| 179 |
+
```
|
| 180 |
+
|
| 181 |
+
### Utilization Rates
|
| 182 |
+
```
|
| 183 |
+
Avg Operational Hours/Train: 16.20 hours/day
|
| 184 |
+
Avg Idle Hours/Train: 7.80 hours/day
|
| 185 |
+
Utilization Rate: 67.5%
|
| 186 |
+
```
|
| 187 |
+
|
| 188 |
+
## Visualization Tips for Paper
|
| 189 |
+
|
| 190 |
+
### Suggested Graphs/Tables
|
| 191 |
+
|
| 192 |
+
1. **Fleet Size vs. Coverage Graph**
|
| 193 |
+
- X-axis: Fleet Size (10-40 trains)
|
| 194 |
+
- Y-axis: Coverage Percentage
|
| 195 |
+
- Show peak and off-peak separately
|
| 196 |
+
|
| 197 |
+
2. **Utilization Rate Table**
|
| 198 |
+
```
|
| 199 |
+
Fleet Size | Operational Hours | Idle Hours | Utilization %
|
| 200 |
+
10 | 16.2 | 7.8 | 67.5%
|
| 201 |
+
15 | 16.2 | 7.8 | 67.5%
|
| 202 |
+
...
|
| 203 |
+
```
|
| 204 |
+
|
| 205 |
+
3. **Efficiency Score Comparison**
|
| 206 |
+
- Bar chart showing fleet efficiency vs. cost efficiency
|
| 207 |
+
- Compare different fleet sizes
|
| 208 |
+
|
| 209 |
+
4. **Optimal Fleet Configuration Diagram**
|
| 210 |
+
- Visual breakdown of train allocation
|
| 211 |
+
- Service / Standby / Maintenance distribution
|
| 212 |
+
|
| 213 |
+
## Advanced Usage
|
| 214 |
+
|
| 215 |
+
### Sensitivity Analysis
|
| 216 |
+
|
| 217 |
+
```python
|
| 218 |
+
analyzer = FleetUtilizationAnalyzer()
|
| 219 |
+
|
| 220 |
+
# Test different maintenance rates
|
| 221 |
+
for maintenance_rate in [0.05, 0.10, 0.15, 0.20]:
|
| 222 |
+
metrics = analyzer.analyze_fleet_configuration(
|
| 223 |
+
total_fleet=25,
|
| 224 |
+
trains_in_maintenance=int(25 * maintenance_rate)
|
| 225 |
+
)
|
| 226 |
+
print(f"Maintenance {maintenance_rate*100}%: Coverage {metrics.overall_coverage_percent:.1f}%")
|
| 227 |
+
```
|
| 228 |
+
|
| 229 |
+
### Peak Hour Variations
|
| 230 |
+
|
| 231 |
+
```python
|
| 232 |
+
# Modify peak hours
|
| 233 |
+
analyzer.peak_periods = [
|
| 234 |
+
(time(6, 0), time(9, 0)), # Earlier morning peak
|
| 235 |
+
(time(16, 0), time(19, 0)), # Earlier evening peak
|
| 236 |
+
]
|
| 237 |
+
|
| 238 |
+
metrics = analyzer.analyze_fleet_configuration(25, 2)
|
| 239 |
+
```
|
| 240 |
+
|
| 241 |
+
## Integration with Other Benchmarks
|
| 242 |
+
|
| 243 |
+
Combine with schedule generation benchmarks:
|
| 244 |
+
|
| 245 |
+
```python
|
| 246 |
+
from benchmark_schedule_performance import SchedulePerformanceBenchmark
|
| 247 |
+
from benchmark_fleet_utilization import FleetUtilizationBenchmark
|
| 248 |
+
|
| 249 |
+
# Performance benchmark
|
| 250 |
+
perf_benchmark = SchedulePerformanceBenchmark()
|
| 251 |
+
perf_results = perf_benchmark.run_comprehensive_benchmark(
|
| 252 |
+
fleet_sizes=[15, 20, 25, 30],
|
| 253 |
+
num_runs=5
|
| 254 |
+
)
|
| 255 |
+
|
| 256 |
+
# Fleet utilization benchmark
|
| 257 |
+
fleet_benchmark = FleetUtilizationBenchmark()
|
| 258 |
+
fleet_benchmark.run_comprehensive_analysis(
|
| 259 |
+
fleet_sizes=[15, 20, 25, 30]
|
| 260 |
+
)
|
| 261 |
+
|
| 262 |
+
# Cross-reference results for comprehensive analysis
|
| 263 |
+
```
|
| 264 |
+
|
| 265 |
+
## Research Paper Templates
|
| 266 |
+
|
| 267 |
+
### Results Section Template
|
| 268 |
+
|
| 269 |
+
```markdown
|
| 270 |
+
### Fleet Utilization Results
|
| 271 |
+
|
| 272 |
+
#### Minimum Fleet Size
|
| 273 |
+
Analysis of the Kochi Metro route (25.612 km, 22 stations) revealed that a
|
| 274 |
+
minimum fleet of [X] trains is required to maintain target service frequencies.
|
| 275 |
+
This accounts for a [Y]-minute round-trip time, including [Z] minutes of
|
| 276 |
+
turnaround at terminal stations.
|
| 277 |
+
|
| 278 |
+
#### Coverage Efficiency
|
| 279 |
+
Fleet configuration testing demonstrated that:
|
| 280 |
+
- Peak demand (7-10 AM, 5-8 PM) requires [N] trains for 5-minute headways
|
| 281 |
+
- Off-peak periods require [M] trains for 10-minute headways
|
| 282 |
+
- A fleet of [K] trains achieves [P]% overall coverage
|
| 283 |
+
|
| 284 |
+
#### Train Utilization Rate
|
| 285 |
+
Utilization analysis across fleet sizes from 10-40 trains showed:
|
| 286 |
+
- Average operational hours: [H] hours per train per day
|
| 287 |
+
- Average idle time: [I] hours per train per day
|
| 288 |
+
- Overall utilization rate: [U]%
|
| 289 |
+
|
| 290 |
+
These metrics indicate [interpretation of efficiency/optimization].
|
| 291 |
+
```
|
| 292 |
+
|
| 293 |
+
## Troubleshooting
|
| 294 |
+
|
| 295 |
+
### Common Issues
|
| 296 |
+
|
| 297 |
+
1. **Import Errors**: Ensure you're running from the correct directory
|
| 298 |
+
```bash
|
| 299 |
+
cd /path/to/mlservice
|
| 300 |
+
python benchmarks/fleet_utilization/benchmark_fleet_utilization.py
|
| 301 |
+
```
|
| 302 |
+
|
| 303 |
+
2. **Path Issues**: The benchmark automatically handles paths, but if needed:
|
| 304 |
+
```python
|
| 305 |
+
sys.path.insert(0, '/path/to/mlservice')
|
| 306 |
+
```
|
| 307 |
+
|
| 308 |
+
## References
|
| 309 |
+
|
| 310 |
+
- Kochi Metro operational parameters
|
| 311 |
+
- Transit scheduling best practices
|
| 312 |
+
- Fleet optimization literature
|
| 313 |
+
|
| 314 |
+
## Future Enhancements
|
| 315 |
+
|
| 316 |
+
- [ ] Integration with real-time passenger data
|
| 317 |
+
- [ ] Dynamic headway adjustment
|
| 318 |
+
- [ ] Multi-line analysis
|
| 319 |
+
- [ ] Energy consumption correlation
|
| 320 |
+
- [ ] Cost-benefit analysis with operational costs
|
| 321 |
+
|
| 322 |
+
## Support
|
| 323 |
+
|
| 324 |
+
For questions or issues, refer to the main project documentation or examine the example usage in `__main__` blocks of each module.
|
benchmarks/fleet_utilization/__init__.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Fleet Utilization Analysis Module
|
| 3 |
+
|
| 4 |
+
Provides tools for analyzing metro train fleet utilization including:
|
| 5 |
+
- Minimum fleet size calculations
|
| 6 |
+
- Coverage efficiency metrics
|
| 7 |
+
- Train utilization rate analysis
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
from .fleet_analyzer import (
|
| 11 |
+
FleetUtilizationAnalyzer,
|
| 12 |
+
FleetUtilizationMetrics,
|
| 13 |
+
format_metrics_report
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
from .benchmark_fleet_utilization import FleetUtilizationBenchmark
|
| 17 |
+
|
| 18 |
+
__all__ = [
|
| 19 |
+
'FleetUtilizationAnalyzer',
|
| 20 |
+
'FleetUtilizationMetrics',
|
| 21 |
+
'FleetUtilizationBenchmark',
|
| 22 |
+
'format_metrics_report'
|
| 23 |
+
]
|
| 24 |
+
|
| 25 |
+
__version__ = '1.0.0'
|
benchmarks/fleet_utilization/benchmark_fleet_utilization.py
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Comprehensive Fleet Utilization Benchmark
|
| 4 |
+
Generates data for research paper Results section.
|
| 5 |
+
"""
|
| 6 |
+
import json
|
| 7 |
+
import sys
|
| 8 |
+
import os
|
| 9 |
+
from datetime import datetime
|
| 10 |
+
from typing import Dict, List, Any, Optional
|
| 11 |
+
import statistics
|
| 12 |
+
|
| 13 |
+
# Add parent directory to path
|
| 14 |
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
| 15 |
+
|
| 16 |
+
from benchmarks.fleet_utilization.fleet_analyzer import (
|
| 17 |
+
FleetUtilizationAnalyzer,
|
| 18 |
+
FleetUtilizationMetrics,
|
| 19 |
+
format_metrics_report
|
| 20 |
+
)
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class FleetUtilizationBenchmark:
|
| 24 |
+
"""Benchmark fleet utilization across different configurations"""
|
| 25 |
+
|
| 26 |
+
def __init__(self):
|
| 27 |
+
self.analyzer = FleetUtilizationAnalyzer()
|
| 28 |
+
self.results = {
|
| 29 |
+
"metadata": {
|
| 30 |
+
"generated_at": datetime.now().isoformat(),
|
| 31 |
+
"system": "Kochi Metro Rail",
|
| 32 |
+
"analysis_type": "Fleet Utilization"
|
| 33 |
+
},
|
| 34 |
+
"configuration": {
|
| 35 |
+
"route_length_km": self.analyzer.route_length_km,
|
| 36 |
+
"avg_speed_kmh": self.analyzer.avg_speed_kmh,
|
| 37 |
+
"service_hours": self.analyzer.total_service_hours,
|
| 38 |
+
"peak_headway_minutes": self.analyzer.peak_headway_target,
|
| 39 |
+
"offpeak_headway_minutes": self.analyzer.offpeak_headway_target
|
| 40 |
+
},
|
| 41 |
+
"fleet_analyses": [],
|
| 42 |
+
"comparative_analysis": {},
|
| 43 |
+
"optimal_fleet": {}
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
def run_comprehensive_analysis(
|
| 47 |
+
self,
|
| 48 |
+
fleet_sizes: Optional[List[int]] = None,
|
| 49 |
+
maintenance_rate: float = 0.1
|
| 50 |
+
):
|
| 51 |
+
"""
|
| 52 |
+
Run comprehensive fleet utilization analysis.
|
| 53 |
+
|
| 54 |
+
Args:
|
| 55 |
+
fleet_sizes: List of fleet sizes to test (default: 10-40 by 5)
|
| 56 |
+
maintenance_rate: Percentage of fleet in maintenance
|
| 57 |
+
"""
|
| 58 |
+
if fleet_sizes is None:
|
| 59 |
+
fleet_sizes = [10, 15, 20, 25, 30, 35, 40]
|
| 60 |
+
|
| 61 |
+
print("="*70)
|
| 62 |
+
print("COMPREHENSIVE FLEET UTILIZATION BENCHMARK")
|
| 63 |
+
print("="*70)
|
| 64 |
+
print(f"Fleet Sizes to Test: {fleet_sizes}")
|
| 65 |
+
print(f"Maintenance Rate: {maintenance_rate*100:.0f}%")
|
| 66 |
+
print("="*70)
|
| 67 |
+
print()
|
| 68 |
+
|
| 69 |
+
# Analyze each fleet size
|
| 70 |
+
for i, size in enumerate(fleet_sizes, 1):
|
| 71 |
+
print(f"[{i}/{len(fleet_sizes)}] Analyzing fleet size: {size} trains...")
|
| 72 |
+
|
| 73 |
+
maintenance_trains = max(1, int(size * maintenance_rate))
|
| 74 |
+
metrics = self.analyzer.analyze_fleet_configuration(size, maintenance_trains)
|
| 75 |
+
|
| 76 |
+
# Store results
|
| 77 |
+
result_dict = {
|
| 78 |
+
"fleet_size": metrics.fleet_size,
|
| 79 |
+
"minimum_required_trains": metrics.minimum_required_trains,
|
| 80 |
+
"trains_in_service_peak": metrics.trains_in_service_peak,
|
| 81 |
+
"trains_in_service_offpeak": metrics.trains_in_service_offpeak,
|
| 82 |
+
"trains_in_standby": metrics.trains_in_standby,
|
| 83 |
+
"trains_in_maintenance": metrics.trains_in_maintenance,
|
| 84 |
+
"peak_demand_coverage_percent": metrics.peak_demand_coverage_percent,
|
| 85 |
+
"offpeak_demand_coverage_percent": metrics.offpeak_demand_coverage_percent,
|
| 86 |
+
"overall_coverage_percent": metrics.overall_coverage_percent,
|
| 87 |
+
"avg_operational_hours_per_train": metrics.avg_operational_hours_per_train,
|
| 88 |
+
"avg_idle_hours_per_train": metrics.avg_idle_hours_per_train,
|
| 89 |
+
"utilization_rate_percent": metrics.utilization_rate_percent,
|
| 90 |
+
"fleet_efficiency_score": metrics.fleet_efficiency_score,
|
| 91 |
+
"cost_efficiency_score": metrics.cost_efficiency_score
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
self.results["fleet_analyses"].append(result_dict)
|
| 95 |
+
|
| 96 |
+
print(f" ✓ Coverage: {metrics.overall_coverage_percent:.1f}%")
|
| 97 |
+
print(f" ✓ Utilization: {metrics.utilization_rate_percent:.1f}%")
|
| 98 |
+
print(f" ✓ Efficiency: {metrics.fleet_efficiency_score:.1f}/100")
|
| 99 |
+
print()
|
| 100 |
+
|
| 101 |
+
# Comparative analysis
|
| 102 |
+
self._generate_comparative_analysis()
|
| 103 |
+
|
| 104 |
+
# Find optimal fleet
|
| 105 |
+
self._find_optimal_configuration()
|
| 106 |
+
|
| 107 |
+
print("="*70)
|
| 108 |
+
print("ANALYSIS COMPLETE")
|
| 109 |
+
print("="*70)
|
| 110 |
+
|
| 111 |
+
def _generate_comparative_analysis(self):
|
| 112 |
+
"""Generate comparative statistics across all fleet sizes"""
|
| 113 |
+
analyses = self.results["fleet_analyses"]
|
| 114 |
+
|
| 115 |
+
if not analyses:
|
| 116 |
+
return
|
| 117 |
+
|
| 118 |
+
# Extract metrics
|
| 119 |
+
coverage = [a["overall_coverage_percent"] for a in analyses]
|
| 120 |
+
utilization = [a["utilization_rate_percent"] for a in analyses]
|
| 121 |
+
efficiency = [a["fleet_efficiency_score"] for a in analyses]
|
| 122 |
+
|
| 123 |
+
# Find best performers
|
| 124 |
+
best_coverage_idx = coverage.index(max(coverage))
|
| 125 |
+
best_utilization_idx = utilization.index(max(utilization))
|
| 126 |
+
best_efficiency_idx = efficiency.index(max(efficiency))
|
| 127 |
+
|
| 128 |
+
self.results["comparative_analysis"] = {
|
| 129 |
+
"coverage_statistics": {
|
| 130 |
+
"min": min(coverage),
|
| 131 |
+
"max": max(coverage),
|
| 132 |
+
"mean": statistics.mean(coverage),
|
| 133 |
+
"median": statistics.median(coverage),
|
| 134 |
+
"stdev": statistics.stdev(coverage) if len(coverage) > 1 else 0
|
| 135 |
+
},
|
| 136 |
+
"utilization_statistics": {
|
| 137 |
+
"min": min(utilization),
|
| 138 |
+
"max": max(utilization),
|
| 139 |
+
"mean": statistics.mean(utilization),
|
| 140 |
+
"median": statistics.median(utilization),
|
| 141 |
+
"stdev": statistics.stdev(utilization) if len(utilization) > 1 else 0
|
| 142 |
+
},
|
| 143 |
+
"efficiency_statistics": {
|
| 144 |
+
"min": min(efficiency),
|
| 145 |
+
"max": max(efficiency),
|
| 146 |
+
"mean": statistics.mean(efficiency),
|
| 147 |
+
"median": statistics.median(efficiency),
|
| 148 |
+
"stdev": statistics.stdev(efficiency) if len(efficiency) > 1 else 0
|
| 149 |
+
},
|
| 150 |
+
"best_performers": {
|
| 151 |
+
"best_coverage": {
|
| 152 |
+
"fleet_size": analyses[best_coverage_idx]["fleet_size"],
|
| 153 |
+
"coverage_percent": analyses[best_coverage_idx]["overall_coverage_percent"]
|
| 154 |
+
},
|
| 155 |
+
"best_utilization": {
|
| 156 |
+
"fleet_size": analyses[best_utilization_idx]["fleet_size"],
|
| 157 |
+
"utilization_percent": analyses[best_utilization_idx]["utilization_rate_percent"]
|
| 158 |
+
},
|
| 159 |
+
"best_efficiency": {
|
| 160 |
+
"fleet_size": analyses[best_efficiency_idx]["fleet_size"],
|
| 161 |
+
"efficiency_score": analyses[best_efficiency_idx]["fleet_efficiency_score"]
|
| 162 |
+
}
|
| 163 |
+
}
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
def _find_optimal_configuration(self):
|
| 167 |
+
"""Find and store optimal fleet configuration"""
|
| 168 |
+
print("\nFinding optimal fleet configuration...")
|
| 169 |
+
|
| 170 |
+
optimal_size, optimal_metrics = self.analyzer.find_optimal_fleet_size(
|
| 171 |
+
min_coverage_required=95.0
|
| 172 |
+
)
|
| 173 |
+
|
| 174 |
+
self.results["optimal_fleet"] = {
|
| 175 |
+
"optimal_fleet_size": optimal_size,
|
| 176 |
+
"minimum_required_trains": optimal_metrics.minimum_required_trains,
|
| 177 |
+
"coverage_percent": optimal_metrics.overall_coverage_percent,
|
| 178 |
+
"utilization_percent": optimal_metrics.utilization_rate_percent,
|
| 179 |
+
"efficiency_score": optimal_metrics.fleet_efficiency_score,
|
| 180 |
+
"cost_efficiency_score": optimal_metrics.cost_efficiency_score,
|
| 181 |
+
"operational_hours_per_train": optimal_metrics.avg_operational_hours_per_train,
|
| 182 |
+
"idle_hours_per_train": optimal_metrics.avg_idle_hours_per_train
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
print(f" ✓ Optimal Fleet Size: {optimal_size} trains")
|
| 186 |
+
print(f" ✓ Coverage: {optimal_metrics.overall_coverage_percent:.1f}%")
|
| 187 |
+
print(f" ✓ Efficiency: {optimal_metrics.fleet_efficiency_score:.1f}/100")
|
| 188 |
+
|
| 189 |
+
def save_results(self, filename: Optional[str] = None) -> str:
|
| 190 |
+
"""Save results to JSON file"""
|
| 191 |
+
if filename is None:
|
| 192 |
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
| 193 |
+
filename = f"fleet_utilization_benchmark_{timestamp}.json"
|
| 194 |
+
|
| 195 |
+
filepath = os.path.join(
|
| 196 |
+
os.path.dirname(os.path.abspath(__file__)),
|
| 197 |
+
filename
|
| 198 |
+
)
|
| 199 |
+
|
| 200 |
+
with open(filepath, 'w') as f:
|
| 201 |
+
json.dump(self.results, f, indent=2)
|
| 202 |
+
|
| 203 |
+
print(f"\n✓ Results saved to: {filepath}")
|
| 204 |
+
return filepath
|
| 205 |
+
|
| 206 |
+
def generate_report(self, filename: Optional[str] = None) -> str:
|
| 207 |
+
"""Generate human-readable text report"""
|
| 208 |
+
if filename is None:
|
| 209 |
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
| 210 |
+
filename = f"fleet_utilization_report_{timestamp}.txt"
|
| 211 |
+
|
| 212 |
+
filepath = os.path.join(
|
| 213 |
+
os.path.dirname(os.path.abspath(__file__)),
|
| 214 |
+
filename
|
| 215 |
+
)
|
| 216 |
+
|
| 217 |
+
with open(filepath, 'w') as f:
|
| 218 |
+
f.write("="*70 + "\n")
|
| 219 |
+
f.write("FLEET UTILIZATION BENCHMARK REPORT\n")
|
| 220 |
+
f.write("="*70 + "\n\n")
|
| 221 |
+
|
| 222 |
+
# Metadata
|
| 223 |
+
f.write(f"Generated: {self.results['metadata']['generated_at']}\n")
|
| 224 |
+
f.write(f"System: {self.results['metadata']['system']}\n\n")
|
| 225 |
+
|
| 226 |
+
# Configuration
|
| 227 |
+
f.write("Configuration:\n")
|
| 228 |
+
f.write("-"*70 + "\n")
|
| 229 |
+
config = self.results['configuration']
|
| 230 |
+
f.write(f" Route Length: {config['route_length_km']} km\n")
|
| 231 |
+
f.write(f" Average Speed: {config['avg_speed_kmh']} km/h\n")
|
| 232 |
+
f.write(f" Service Hours: {config['service_hours']} hours/day\n")
|
| 233 |
+
f.write(f" Peak Headway Target: {config['peak_headway_minutes']} minutes\n")
|
| 234 |
+
f.write(f" Off-Peak Headway Target: {config['offpeak_headway_minutes']} minutes\n\n")
|
| 235 |
+
|
| 236 |
+
# Optimal Fleet
|
| 237 |
+
f.write("OPTIMAL FLEET CONFIGURATION:\n")
|
| 238 |
+
f.write("-"*70 + "\n")
|
| 239 |
+
optimal = self.results['optimal_fleet']
|
| 240 |
+
f.write(f" Optimal Fleet Size: {optimal['optimal_fleet_size']} trains\n")
|
| 241 |
+
f.write(f" Minimum Required: {optimal['minimum_required_trains']} trains\n")
|
| 242 |
+
f.write(f" Coverage: {optimal['coverage_percent']:.1f}%\n")
|
| 243 |
+
f.write(f" Utilization Rate: {optimal['utilization_percent']:.1f}%\n")
|
| 244 |
+
f.write(f" Fleet Efficiency: {optimal['efficiency_score']:.1f}/100\n")
|
| 245 |
+
f.write(f" Operational Hours/Train: {optimal['operational_hours_per_train']:.2f} hrs/day\n")
|
| 246 |
+
f.write(f" Idle Hours/Train: {optimal['idle_hours_per_train']:.2f} hrs/day\n\n")
|
| 247 |
+
|
| 248 |
+
# Comparative Analysis
|
| 249 |
+
f.write("COMPARATIVE ANALYSIS:\n")
|
| 250 |
+
f.write("-"*70 + "\n")
|
| 251 |
+
comp = self.results['comparative_analysis']
|
| 252 |
+
|
| 253 |
+
f.write("\nCoverage Statistics:\n")
|
| 254 |
+
stats = comp['coverage_statistics']
|
| 255 |
+
f.write(f" Mean: {stats['mean']:.2f}%\n")
|
| 256 |
+
f.write(f" Median: {stats['median']:.2f}%\n")
|
| 257 |
+
f.write(f" Range: {stats['min']:.2f}% - {stats['max']:.2f}%\n")
|
| 258 |
+
f.write(f" Std Dev: {stats['stdev']:.2f}\n")
|
| 259 |
+
|
| 260 |
+
f.write("\nUtilization Statistics:\n")
|
| 261 |
+
stats = comp['utilization_statistics']
|
| 262 |
+
f.write(f" Mean: {stats['mean']:.2f}%\n")
|
| 263 |
+
f.write(f" Median: {stats['median']:.2f}%\n")
|
| 264 |
+
f.write(f" Range: {stats['min']:.2f}% - {stats['max']:.2f}%\n")
|
| 265 |
+
f.write(f" Std Dev: {stats['stdev']:.2f}\n")
|
| 266 |
+
|
| 267 |
+
f.write("\nEfficiency Statistics:\n")
|
| 268 |
+
stats = comp['efficiency_statistics']
|
| 269 |
+
f.write(f" Mean: {stats['mean']:.2f}/100\n")
|
| 270 |
+
f.write(f" Median: {stats['median']:.2f}/100\n")
|
| 271 |
+
f.write(f" Range: {stats['min']:.2f} - {stats['max']:.2f}\n")
|
| 272 |
+
f.write(f" Std Dev: {stats['stdev']:.2f}\n")
|
| 273 |
+
|
| 274 |
+
# Best Performers
|
| 275 |
+
f.write("\nBest Performers:\n")
|
| 276 |
+
best = comp['best_performers']
|
| 277 |
+
f.write(f" Best Coverage: {best['best_coverage']['fleet_size']} trains ({best['best_coverage']['coverage_percent']:.1f}%)\n")
|
| 278 |
+
f.write(f" Best Utilization: {best['best_utilization']['fleet_size']} trains ({best['best_utilization']['utilization_percent']:.1f}%)\n")
|
| 279 |
+
f.write(f" Best Efficiency: {best['best_efficiency']['fleet_size']} trains ({best['best_efficiency']['efficiency_score']:.1f}/100)\n\n")
|
| 280 |
+
|
| 281 |
+
# Detailed Results
|
| 282 |
+
f.write("DETAILED FLEET ANALYSES:\n")
|
| 283 |
+
f.write("="*70 + "\n\n")
|
| 284 |
+
|
| 285 |
+
for analysis in self.results['fleet_analyses']:
|
| 286 |
+
f.write(f"Fleet Size: {analysis['fleet_size']} trains\n")
|
| 287 |
+
f.write("-"*70 + "\n")
|
| 288 |
+
f.write(f" Minimum Required: {analysis['minimum_required_trains']} trains\n")
|
| 289 |
+
f.write(f" Peak Service: {analysis['trains_in_service_peak']} trains\n")
|
| 290 |
+
f.write(f" Off-Peak Service: {analysis['trains_in_service_offpeak']} trains\n")
|
| 291 |
+
f.write(f" Standby: {analysis['trains_in_standby']} trains\n")
|
| 292 |
+
f.write(f" Maintenance: {analysis['trains_in_maintenance']} trains\n")
|
| 293 |
+
f.write(f" Peak Coverage: {analysis['peak_demand_coverage_percent']:.1f}%\n")
|
| 294 |
+
f.write(f" Off-Peak Coverage: {analysis['offpeak_demand_coverage_percent']:.1f}%\n")
|
| 295 |
+
f.write(f" Overall Coverage: {analysis['overall_coverage_percent']:.1f}%\n")
|
| 296 |
+
f.write(f" Operational Hours/Train: {analysis['avg_operational_hours_per_train']:.2f} hrs\n")
|
| 297 |
+
f.write(f" Idle Hours/Train: {analysis['avg_idle_hours_per_train']:.2f} hrs\n")
|
| 298 |
+
f.write(f" Utilization Rate: {analysis['utilization_rate_percent']:.1f}%\n")
|
| 299 |
+
f.write(f" Fleet Efficiency: {analysis['fleet_efficiency_score']:.1f}/100\n")
|
| 300 |
+
f.write(f" Cost Efficiency: {analysis['cost_efficiency_score']:.1f}/100\n")
|
| 301 |
+
f.write("\n")
|
| 302 |
+
|
| 303 |
+
print(f"✓ Report saved to: {filepath}")
|
| 304 |
+
return filepath
|
| 305 |
+
|
| 306 |
+
|
| 307 |
+
def main():
|
| 308 |
+
"""Run comprehensive fleet utilization benchmark"""
|
| 309 |
+
benchmark = FleetUtilizationBenchmark()
|
| 310 |
+
|
| 311 |
+
# Run analysis for various fleet sizes
|
| 312 |
+
benchmark.run_comprehensive_analysis(
|
| 313 |
+
fleet_sizes=[10, 15, 20, 25, 30, 35, 40],
|
| 314 |
+
maintenance_rate=0.1
|
| 315 |
+
)
|
| 316 |
+
|
| 317 |
+
# Save results
|
| 318 |
+
benchmark.save_results()
|
| 319 |
+
benchmark.generate_report()
|
| 320 |
+
|
| 321 |
+
print("\n" + "="*70)
|
| 322 |
+
print("BENCHMARK COMPLETE")
|
| 323 |
+
print("="*70)
|
| 324 |
+
print("\nFiles generated:")
|
| 325 |
+
print(" - fleet_utilization_benchmark_TIMESTAMP.json")
|
| 326 |
+
print(" - fleet_utilization_report_TIMESTAMP.txt")
|
| 327 |
+
print("\nUse these results for your research paper Results section!")
|
| 328 |
+
|
| 329 |
+
|
| 330 |
+
if __name__ == "__main__":
|
| 331 |
+
main()
|
benchmarks/fleet_utilization/fleet_analyzer.py
ADDED
|
@@ -0,0 +1,401 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Fleet Utilization Analysis for Metro Train Scheduling
|
| 4 |
+
Analyzes minimum fleet size, coverage efficiency, and train utilization rates.
|
| 5 |
+
"""
|
| 6 |
+
from typing import Dict, List, Tuple, Any, Optional
|
| 7 |
+
from datetime import datetime, time, timedelta
|
| 8 |
+
import statistics
|
| 9 |
+
from dataclasses import dataclass
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
@dataclass
|
| 13 |
+
class FleetUtilizationMetrics:
|
| 14 |
+
"""Metrics for fleet utilization analysis"""
|
| 15 |
+
fleet_size: int
|
| 16 |
+
minimum_required_trains: int
|
| 17 |
+
trains_in_service_peak: int
|
| 18 |
+
trains_in_service_offpeak: int
|
| 19 |
+
trains_in_standby: int
|
| 20 |
+
trains_in_maintenance: int
|
| 21 |
+
|
| 22 |
+
# Coverage metrics
|
| 23 |
+
peak_demand_coverage_percent: float
|
| 24 |
+
offpeak_demand_coverage_percent: float
|
| 25 |
+
overall_coverage_percent: float
|
| 26 |
+
|
| 27 |
+
# Utilization metrics
|
| 28 |
+
avg_operational_hours_per_train: float
|
| 29 |
+
avg_idle_hours_per_train: float
|
| 30 |
+
utilization_rate_percent: float
|
| 31 |
+
|
| 32 |
+
# Time distribution
|
| 33 |
+
total_service_hours: float
|
| 34 |
+
peak_hours_duration: float
|
| 35 |
+
offpeak_hours_duration: float
|
| 36 |
+
|
| 37 |
+
# Efficiency scores
|
| 38 |
+
fleet_efficiency_score: float
|
| 39 |
+
cost_efficiency_score: float
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
class FleetUtilizationAnalyzer:
|
| 43 |
+
"""Analyzes fleet utilization for metro scheduling optimization"""
|
| 44 |
+
|
| 45 |
+
def __init__(self):
|
| 46 |
+
# Kochi Metro operational parameters
|
| 47 |
+
self.service_start = time(5, 0) # 5:00 AM
|
| 48 |
+
self.service_end = time(23, 0) # 11:00 PM
|
| 49 |
+
self.total_service_hours = 18.0 # 18 hours per day
|
| 50 |
+
|
| 51 |
+
# Peak hours definition
|
| 52 |
+
self.peak_periods = [
|
| 53 |
+
(time(7, 0), time(10, 0)), # Morning peak: 7-10 AM
|
| 54 |
+
(time(17, 0), time(20, 0)), # Evening peak: 5-8 PM
|
| 55 |
+
]
|
| 56 |
+
|
| 57 |
+
# Target headways (minutes between trains)
|
| 58 |
+
self.peak_headway_target = 5 # 5 minutes during peak
|
| 59 |
+
self.offpeak_headway_target = 10 # 10 minutes during off-peak
|
| 60 |
+
|
| 61 |
+
# Route parameters (Kochi Metro)
|
| 62 |
+
self.route_length_km = 25.612
|
| 63 |
+
self.avg_speed_kmh = 35
|
| 64 |
+
self.turnaround_time_minutes = 10
|
| 65 |
+
|
| 66 |
+
# Calculate round trip time
|
| 67 |
+
self.one_way_time = (self.route_length_km / self.avg_speed_kmh) * 60 # minutes
|
| 68 |
+
self.round_trip_time = (self.one_way_time * 2) + (self.turnaround_time_minutes * 2)
|
| 69 |
+
|
| 70 |
+
def calculate_peak_hours_duration(self) -> float:
|
| 71 |
+
"""Calculate total peak hours per day"""
|
| 72 |
+
total_peak_minutes = 0
|
| 73 |
+
for start, end in self.peak_periods:
|
| 74 |
+
start_minutes = start.hour * 60 + start.minute
|
| 75 |
+
end_minutes = end.hour * 60 + end.minute
|
| 76 |
+
total_peak_minutes += (end_minutes - start_minutes)
|
| 77 |
+
return total_peak_minutes / 60.0 # Convert to hours
|
| 78 |
+
|
| 79 |
+
def calculate_minimum_fleet_size(
|
| 80 |
+
self,
|
| 81 |
+
headway_minutes: int,
|
| 82 |
+
round_trip_minutes: Optional[float] = None
|
| 83 |
+
) -> int:
|
| 84 |
+
"""
|
| 85 |
+
Calculate minimum number of trains needed to maintain headway.
|
| 86 |
+
|
| 87 |
+
Formula: Minimum Fleet = (Round Trip Time / Headway) + Buffer
|
| 88 |
+
|
| 89 |
+
Args:
|
| 90 |
+
headway_minutes: Desired minutes between trains
|
| 91 |
+
round_trip_minutes: Optional override for round trip time
|
| 92 |
+
|
| 93 |
+
Returns:
|
| 94 |
+
Minimum number of trains required
|
| 95 |
+
"""
|
| 96 |
+
rtt = round_trip_minutes if round_trip_minutes else self.round_trip_time
|
| 97 |
+
|
| 98 |
+
# Calculate base requirement
|
| 99 |
+
base_trains = rtt / headway_minutes
|
| 100 |
+
|
| 101 |
+
# Add buffer for operational flexibility (1 train) and maintenance (10%)
|
| 102 |
+
buffer_trains = 1
|
| 103 |
+
maintenance_buffer = max(1, int(base_trains * 0.1))
|
| 104 |
+
|
| 105 |
+
minimum_fleet = int(base_trains) + buffer_trains + maintenance_buffer
|
| 106 |
+
|
| 107 |
+
return minimum_fleet
|
| 108 |
+
|
| 109 |
+
def calculate_demand_coverage(
|
| 110 |
+
self,
|
| 111 |
+
available_trains: int,
|
| 112 |
+
required_trains_peak: int,
|
| 113 |
+
required_trains_offpeak: int
|
| 114 |
+
) -> Dict[str, float]:
|
| 115 |
+
"""
|
| 116 |
+
Calculate what percentage of demand can be covered.
|
| 117 |
+
|
| 118 |
+
Args:
|
| 119 |
+
available_trains: Number of trains available for service
|
| 120 |
+
required_trains_peak: Required trains during peak hours
|
| 121 |
+
required_trains_offpeak: Required trains during off-peak hours
|
| 122 |
+
|
| 123 |
+
Returns:
|
| 124 |
+
Dictionary with coverage percentages
|
| 125 |
+
"""
|
| 126 |
+
peak_coverage = min(100.0, (available_trains / required_trains_peak) * 100)
|
| 127 |
+
offpeak_coverage = min(100.0, (available_trains / required_trains_offpeak) * 100)
|
| 128 |
+
|
| 129 |
+
# Weight by duration
|
| 130 |
+
peak_duration = self.calculate_peak_hours_duration()
|
| 131 |
+
offpeak_duration = self.total_service_hours - peak_duration
|
| 132 |
+
|
| 133 |
+
overall_coverage = (
|
| 134 |
+
(peak_coverage * peak_duration + offpeak_coverage * offpeak_duration)
|
| 135 |
+
/ self.total_service_hours
|
| 136 |
+
)
|
| 137 |
+
|
| 138 |
+
return {
|
| 139 |
+
"peak_coverage_percent": round(peak_coverage, 2),
|
| 140 |
+
"offpeak_coverage_percent": round(offpeak_coverage, 2),
|
| 141 |
+
"overall_coverage_percent": round(overall_coverage, 2)
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
def calculate_train_utilization(
|
| 145 |
+
self,
|
| 146 |
+
trains_in_service: int,
|
| 147 |
+
service_hours: Optional[float] = None
|
| 148 |
+
) -> Dict[str, float]:
|
| 149 |
+
"""
|
| 150 |
+
Calculate average operational hours and utilization per train.
|
| 151 |
+
|
| 152 |
+
Args:
|
| 153 |
+
trains_in_service: Number of trains actively in service
|
| 154 |
+
service_hours: Total service hours (default: full day)
|
| 155 |
+
|
| 156 |
+
Returns:
|
| 157 |
+
Dictionary with utilization metrics
|
| 158 |
+
"""
|
| 159 |
+
service_hours = service_hours or self.total_service_hours
|
| 160 |
+
|
| 161 |
+
# Average operational hours per train
|
| 162 |
+
# Assumes trains operate in shifts to cover full service period
|
| 163 |
+
avg_operational_hours = service_hours * 0.9 # 90% active time assumption
|
| 164 |
+
|
| 165 |
+
# Idle hours
|
| 166 |
+
avg_idle_hours = 24 - avg_operational_hours
|
| 167 |
+
|
| 168 |
+
# Utilization rate
|
| 169 |
+
utilization_rate = (avg_operational_hours / 24) * 100
|
| 170 |
+
|
| 171 |
+
return {
|
| 172 |
+
"avg_operational_hours": round(avg_operational_hours, 2),
|
| 173 |
+
"avg_idle_hours": round(avg_idle_hours, 2),
|
| 174 |
+
"utilization_rate_percent": round(utilization_rate, 2)
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
def calculate_fleet_efficiency_score(
|
| 178 |
+
self,
|
| 179 |
+
fleet_size: int,
|
| 180 |
+
minimum_required: int,
|
| 181 |
+
coverage_percent: float
|
| 182 |
+
) -> float:
|
| 183 |
+
"""
|
| 184 |
+
Calculate overall fleet efficiency score (0-100).
|
| 185 |
+
|
| 186 |
+
Higher score = better efficiency
|
| 187 |
+
Considers fleet size optimization and coverage
|
| 188 |
+
|
| 189 |
+
Args:
|
| 190 |
+
fleet_size: Actual fleet size
|
| 191 |
+
minimum_required: Minimum required trains
|
| 192 |
+
coverage_percent: Overall demand coverage percentage
|
| 193 |
+
|
| 194 |
+
Returns:
|
| 195 |
+
Efficiency score (0-100)
|
| 196 |
+
"""
|
| 197 |
+
# Penalty for excess fleet (cost inefficiency)
|
| 198 |
+
excess_ratio = (fleet_size - minimum_required) / minimum_required
|
| 199 |
+
excess_penalty = min(30, excess_ratio * 20) # Max 30 point penalty
|
| 200 |
+
|
| 201 |
+
# Reward for coverage (service quality)
|
| 202 |
+
coverage_score = coverage_percent * 0.7 # 70% weight on coverage
|
| 203 |
+
|
| 204 |
+
# Efficiency score
|
| 205 |
+
efficiency = coverage_score - excess_penalty
|
| 206 |
+
efficiency = max(0, min(100, efficiency)) # Clamp to 0-100
|
| 207 |
+
|
| 208 |
+
return round(efficiency, 2)
|
| 209 |
+
|
| 210 |
+
def analyze_fleet_configuration(
|
| 211 |
+
self,
|
| 212 |
+
total_fleet: int,
|
| 213 |
+
trains_in_maintenance: int = 0,
|
| 214 |
+
trains_reserved: int = 0
|
| 215 |
+
) -> FleetUtilizationMetrics:
|
| 216 |
+
"""
|
| 217 |
+
Comprehensive analysis of a fleet configuration.
|
| 218 |
+
|
| 219 |
+
Args:
|
| 220 |
+
total_fleet: Total number of trains in fleet
|
| 221 |
+
trains_in_maintenance: Trains currently in maintenance
|
| 222 |
+
trains_reserved: Trains reserved/held back
|
| 223 |
+
|
| 224 |
+
Returns:
|
| 225 |
+
FleetUtilizationMetrics object with complete analysis
|
| 226 |
+
"""
|
| 227 |
+
# Calculate available trains
|
| 228 |
+
available_trains = total_fleet - trains_in_maintenance - trains_reserved
|
| 229 |
+
|
| 230 |
+
# Calculate minimum requirements
|
| 231 |
+
min_fleet_peak = self.calculate_minimum_fleet_size(self.peak_headway_target)
|
| 232 |
+
min_fleet_offpeak = self.calculate_minimum_fleet_size(self.offpeak_headway_target)
|
| 233 |
+
min_fleet_overall = max(min_fleet_peak, min_fleet_offpeak)
|
| 234 |
+
|
| 235 |
+
# Determine actual service allocation
|
| 236 |
+
trains_in_service_peak = min(available_trains, min_fleet_peak)
|
| 237 |
+
trains_in_service_offpeak = min(available_trains, min_fleet_offpeak)
|
| 238 |
+
trains_in_standby = max(0, available_trains - trains_in_service_peak)
|
| 239 |
+
|
| 240 |
+
# Coverage analysis
|
| 241 |
+
coverage = self.calculate_demand_coverage(
|
| 242 |
+
available_trains,
|
| 243 |
+
min_fleet_peak,
|
| 244 |
+
min_fleet_offpeak
|
| 245 |
+
)
|
| 246 |
+
|
| 247 |
+
# Utilization analysis
|
| 248 |
+
utilization = self.calculate_train_utilization(trains_in_service_peak)
|
| 249 |
+
|
| 250 |
+
# Efficiency scores
|
| 251 |
+
peak_duration = self.calculate_peak_hours_duration()
|
| 252 |
+
offpeak_duration = self.total_service_hours - peak_duration
|
| 253 |
+
|
| 254 |
+
fleet_efficiency = self.calculate_fleet_efficiency_score(
|
| 255 |
+
total_fleet,
|
| 256 |
+
min_fleet_overall,
|
| 257 |
+
coverage["overall_coverage_percent"]
|
| 258 |
+
)
|
| 259 |
+
|
| 260 |
+
# Cost efficiency (fewer trains = better cost efficiency, but must meet demand)
|
| 261 |
+
cost_efficiency = (min_fleet_overall / total_fleet) * coverage["overall_coverage_percent"]
|
| 262 |
+
|
| 263 |
+
return FleetUtilizationMetrics(
|
| 264 |
+
fleet_size=total_fleet,
|
| 265 |
+
minimum_required_trains=min_fleet_overall,
|
| 266 |
+
trains_in_service_peak=trains_in_service_peak,
|
| 267 |
+
trains_in_service_offpeak=trains_in_service_offpeak,
|
| 268 |
+
trains_in_standby=trains_in_standby,
|
| 269 |
+
trains_in_maintenance=trains_in_maintenance,
|
| 270 |
+
peak_demand_coverage_percent=coverage["peak_coverage_percent"],
|
| 271 |
+
offpeak_demand_coverage_percent=coverage["offpeak_coverage_percent"],
|
| 272 |
+
overall_coverage_percent=coverage["overall_coverage_percent"],
|
| 273 |
+
avg_operational_hours_per_train=utilization["avg_operational_hours"],
|
| 274 |
+
avg_idle_hours_per_train=utilization["avg_idle_hours"],
|
| 275 |
+
utilization_rate_percent=utilization["utilization_rate_percent"],
|
| 276 |
+
total_service_hours=self.total_service_hours,
|
| 277 |
+
peak_hours_duration=peak_duration,
|
| 278 |
+
offpeak_hours_duration=offpeak_duration,
|
| 279 |
+
fleet_efficiency_score=fleet_efficiency,
|
| 280 |
+
cost_efficiency_score=round(cost_efficiency, 2)
|
| 281 |
+
)
|
| 282 |
+
|
| 283 |
+
def compare_fleet_sizes(
|
| 284 |
+
self,
|
| 285 |
+
fleet_sizes: List[int],
|
| 286 |
+
maintenance_rate: float = 0.1
|
| 287 |
+
) -> Dict[int, FleetUtilizationMetrics]:
|
| 288 |
+
"""
|
| 289 |
+
Compare different fleet size configurations.
|
| 290 |
+
|
| 291 |
+
Args:
|
| 292 |
+
fleet_sizes: List of fleet sizes to analyze
|
| 293 |
+
maintenance_rate: Percentage of fleet in maintenance (default 10%)
|
| 294 |
+
|
| 295 |
+
Returns:
|
| 296 |
+
Dictionary mapping fleet size to metrics
|
| 297 |
+
"""
|
| 298 |
+
results = {}
|
| 299 |
+
|
| 300 |
+
for size in fleet_sizes:
|
| 301 |
+
maintenance_trains = max(1, int(size * maintenance_rate))
|
| 302 |
+
metrics = self.analyze_fleet_configuration(size, maintenance_trains)
|
| 303 |
+
results[size] = metrics
|
| 304 |
+
|
| 305 |
+
return results
|
| 306 |
+
|
| 307 |
+
def find_optimal_fleet_size(
|
| 308 |
+
self,
|
| 309 |
+
min_coverage_required: float = 95.0,
|
| 310 |
+
max_fleet: int = 50
|
| 311 |
+
) -> Tuple[int, FleetUtilizationMetrics]:
|
| 312 |
+
"""
|
| 313 |
+
Find the optimal (smallest) fleet size that meets coverage requirements.
|
| 314 |
+
|
| 315 |
+
Args:
|
| 316 |
+
min_coverage_required: Minimum acceptable coverage percentage
|
| 317 |
+
max_fleet: Maximum fleet size to consider
|
| 318 |
+
|
| 319 |
+
Returns:
|
| 320 |
+
Tuple of (optimal_fleet_size, metrics)
|
| 321 |
+
"""
|
| 322 |
+
# Start from minimum required and increment
|
| 323 |
+
min_theoretical = self.calculate_minimum_fleet_size(self.peak_headway_target)
|
| 324 |
+
|
| 325 |
+
for fleet_size in range(min_theoretical, max_fleet + 1):
|
| 326 |
+
maintenance_trains = max(1, int(fleet_size * 0.1))
|
| 327 |
+
metrics = self.analyze_fleet_configuration(fleet_size, maintenance_trains)
|
| 328 |
+
|
| 329 |
+
if metrics.overall_coverage_percent >= min_coverage_required:
|
| 330 |
+
return fleet_size, metrics
|
| 331 |
+
|
| 332 |
+
# If no solution found, return largest tested
|
| 333 |
+
metrics = self.analyze_fleet_configuration(max_fleet, int(max_fleet * 0.1))
|
| 334 |
+
return max_fleet, metrics
|
| 335 |
+
|
| 336 |
+
|
| 337 |
+
def format_metrics_report(metrics: FleetUtilizationMetrics) -> str:
|
| 338 |
+
"""Format metrics into a readable report"""
|
| 339 |
+
report = f"""
|
| 340 |
+
{'='*70}
|
| 341 |
+
FLEET UTILIZATION ANALYSIS REPORT
|
| 342 |
+
{'='*70}
|
| 343 |
+
|
| 344 |
+
Fleet Configuration:
|
| 345 |
+
Total Fleet Size: {metrics.fleet_size} trains
|
| 346 |
+
Minimum Required: {metrics.minimum_required_trains} trains
|
| 347 |
+
Excess Capacity: {metrics.fleet_size - metrics.minimum_required_trains} trains
|
| 348 |
+
|
| 349 |
+
Service Allocation:
|
| 350 |
+
Peak Service: {metrics.trains_in_service_peak} trains
|
| 351 |
+
Off-Peak Service: {metrics.trains_in_service_offpeak} trains
|
| 352 |
+
Standby: {metrics.trains_in_standby} trains
|
| 353 |
+
Maintenance: {metrics.trains_in_maintenance} trains
|
| 354 |
+
|
| 355 |
+
Coverage Efficiency:
|
| 356 |
+
Peak Demand Coverage: {metrics.peak_demand_coverage_percent:.1f}%
|
| 357 |
+
Off-Peak Demand Coverage: {metrics.offpeak_demand_coverage_percent:.1f}%
|
| 358 |
+
Overall Coverage: {metrics.overall_coverage_percent:.1f}%
|
| 359 |
+
|
| 360 |
+
Train Utilization:
|
| 361 |
+
Avg Operational Hours/Train: {metrics.avg_operational_hours_per_train:.2f} hours/day
|
| 362 |
+
Avg Idle Hours/Train: {metrics.avg_idle_hours_per_train:.2f} hours/day
|
| 363 |
+
Utilization Rate: {metrics.utilization_rate_percent:.1f}%
|
| 364 |
+
|
| 365 |
+
Time Distribution:
|
| 366 |
+
Total Service Hours: {metrics.total_service_hours:.1f} hours/day
|
| 367 |
+
Peak Hours: {metrics.peak_hours_duration:.1f} hours/day
|
| 368 |
+
Off-Peak Hours: {metrics.offpeak_hours_duration:.1f} hours/day
|
| 369 |
+
|
| 370 |
+
Efficiency Scores:
|
| 371 |
+
Fleet Efficiency: {metrics.fleet_efficiency_score:.1f}/100
|
| 372 |
+
Cost Efficiency: {metrics.cost_efficiency_score:.1f}/100
|
| 373 |
+
|
| 374 |
+
{'='*70}
|
| 375 |
+
"""
|
| 376 |
+
return report
|
| 377 |
+
|
| 378 |
+
|
| 379 |
+
if __name__ == "__main__":
|
| 380 |
+
# Example usage
|
| 381 |
+
analyzer = FleetUtilizationAnalyzer()
|
| 382 |
+
|
| 383 |
+
print("Kochi Metro Fleet Utilization Analysis")
|
| 384 |
+
print("=" * 70)
|
| 385 |
+
|
| 386 |
+
# Analyze specific fleet size
|
| 387 |
+
metrics = analyzer.analyze_fleet_configuration(
|
| 388 |
+
total_fleet=25,
|
| 389 |
+
trains_in_maintenance=2
|
| 390 |
+
)
|
| 391 |
+
|
| 392 |
+
print(format_metrics_report(metrics))
|
| 393 |
+
|
| 394 |
+
# Find optimal fleet size
|
| 395 |
+
optimal_size, optimal_metrics = analyzer.find_optimal_fleet_size(
|
| 396 |
+
min_coverage_required=95.0
|
| 397 |
+
)
|
| 398 |
+
|
| 399 |
+
print(f"\nOptimal Fleet Size: {optimal_size} trains")
|
| 400 |
+
print(f"Coverage: {optimal_metrics.overall_coverage_percent:.1f}%")
|
| 401 |
+
print(f"Efficiency Score: {optimal_metrics.fleet_efficiency_score:.1f}/100")
|
example_benchmark.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Simple example of running the benchmark
|
| 4 |
+
This demonstrates how to collect performance data for a research paper
|
| 5 |
+
|
| 6 |
+
NOTE: The benchmark uses EnhancedMetroDataGenerator from DataService
|
| 7 |
+
to create complete, realistic synthetic data for testing greedy optimizers.
|
| 8 |
+
"""
|
| 9 |
+
import sys
|
| 10 |
+
import os
|
| 11 |
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
| 12 |
+
|
| 13 |
+
from benchmark_schedule_performance import SchedulePerformanceBenchmark
|
| 14 |
+
|
| 15 |
+
def run_simple_benchmark():
|
| 16 |
+
"""Run a simple benchmark for demonstration"""
|
| 17 |
+
print("="*70)
|
| 18 |
+
print("SIMPLE BENCHMARK EXAMPLE")
|
| 19 |
+
print("="*70)
|
| 20 |
+
print("\nThis example demonstrates benchmarking for research paper results.")
|
| 21 |
+
print("It tests 3 fleet sizes with 2 runs each.")
|
| 22 |
+
print("\nNOTE: Synthetic data is generated using DataService/EnhancedMetroDataGenerator")
|
| 23 |
+
print(" This includes trainset status, fitness certificates, job cards, etc.\n")
|
| 24 |
+
|
| 25 |
+
# Create benchmark instance
|
| 26 |
+
benchmark = SchedulePerformanceBenchmark()
|
| 27 |
+
|
| 28 |
+
# Run benchmark with small configuration
|
| 29 |
+
benchmark.run_comprehensive_benchmark(
|
| 30 |
+
fleet_sizes=[10, 20, 30], # Test with 10, 20, and 30 trains
|
| 31 |
+
greedy_methods=['ga', 'pso'], # Test GA and PSO
|
| 32 |
+
num_runs=2 # 2 runs for quick results
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
# Save results
|
| 36 |
+
json_file = benchmark.save_results()
|
| 37 |
+
report_file = benchmark.generate_report()
|
| 38 |
+
|
| 39 |
+
print("\n" + "="*70)
|
| 40 |
+
print("RESULTS FOR YOUR PAPER")
|
| 41 |
+
print("="*70)
|
| 42 |
+
|
| 43 |
+
if "summary" in benchmark.results and "fastest_optimizer" in benchmark.results["summary"]:
|
| 44 |
+
fastest = benchmark.results["summary"]["fastest_optimizer"]
|
| 45 |
+
fastest_time = benchmark.results["summary"]["fastest_time_seconds"]
|
| 46 |
+
print(f"\nFastest Method: {fastest}")
|
| 47 |
+
print(f"Average Time: {fastest_time:.4f} seconds")
|
| 48 |
+
|
| 49 |
+
print("\nYou can use the following data in your Results section:")
|
| 50 |
+
print(f"- Detailed JSON data: {json_file}")
|
| 51 |
+
print(f"- Formatted report: {report_file}")
|
| 52 |
+
|
| 53 |
+
print("\n" + "="*70)
|
| 54 |
+
|
| 55 |
+
if __name__ == "__main__":
|
| 56 |
+
run_simple_benchmark()
|
greedyOptim/error_handling.py
CHANGED
|
@@ -125,7 +125,7 @@ class DataValidator:
|
|
| 125 |
|
| 126 |
# Validate dates
|
| 127 |
for date_field in ['issue_date', 'expiry_date']:
|
| 128 |
-
if date_field in record:
|
| 129 |
try:
|
| 130 |
datetime.fromisoformat(record[date_field])
|
| 131 |
except ValueError:
|
|
|
|
| 125 |
|
| 126 |
# Validate dates
|
| 127 |
for date_field in ['issue_date', 'expiry_date']:
|
| 128 |
+
if date_field in record and record[date_field] is not None:
|
| 129 |
try:
|
| 130 |
datetime.fromisoformat(record[date_field])
|
| 131 |
except ValueError:
|
schedule_benchmark_20251106_134014.json
ADDED
|
@@ -0,0 +1,838 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"benchmark_info": {
|
| 3 |
+
"date": "2025-11-06T13:37:15.527707",
|
| 4 |
+
"description": "Metro Schedule Generation Performance Analysis",
|
| 5 |
+
"test_type": "Schedule Generation Time & Computational Efficiency"
|
| 6 |
+
},
|
| 7 |
+
"configurations": [
|
| 8 |
+
{
|
| 9 |
+
"fleet_sizes": [
|
| 10 |
+
10,
|
| 11 |
+
15,
|
| 12 |
+
20,
|
| 13 |
+
25,
|
| 14 |
+
30,
|
| 15 |
+
40
|
| 16 |
+
],
|
| 17 |
+
"greedy_methods": [
|
| 18 |
+
"ga",
|
| 19 |
+
"pso",
|
| 20 |
+
"cmaes"
|
| 21 |
+
],
|
| 22 |
+
"runs_per_config": 5,
|
| 23 |
+
"station_count": 22
|
| 24 |
+
}
|
| 25 |
+
],
|
| 26 |
+
"detailed_results": [
|
| 27 |
+
{
|
| 28 |
+
"optimizer": "MetroScheduleOptimizer",
|
| 29 |
+
"fleet_size": 10,
|
| 30 |
+
"num_stations": 22,
|
| 31 |
+
"num_runs": 5,
|
| 32 |
+
"successful_runs": 5,
|
| 33 |
+
"success_rate": "100.0%",
|
| 34 |
+
"execution_time": {
|
| 35 |
+
"min_seconds": 0.000391669000237016,
|
| 36 |
+
"max_seconds": 0.0006529950001095131,
|
| 37 |
+
"mean_seconds": 0.0004975758000909991,
|
| 38 |
+
"median_seconds": 0.00047151700027825427,
|
| 39 |
+
"stdev_seconds": 0.00010852079857594056,
|
| 40 |
+
"all_runs_seconds": [
|
| 41 |
+
0.0006529950001095131,
|
| 42 |
+
0.0005592709999291401,
|
| 43 |
+
0.0004124269999010721,
|
| 44 |
+
0.000391669000237016,
|
| 45 |
+
0.00047151700027825427
|
| 46 |
+
]
|
| 47 |
+
},
|
| 48 |
+
"schedule_statistics": {
|
| 49 |
+
"num_trainsets": {
|
| 50 |
+
"mean": 10,
|
| 51 |
+
"min": 10,
|
| 52 |
+
"max": 10
|
| 53 |
+
},
|
| 54 |
+
"num_in_service": {
|
| 55 |
+
"mean": 0,
|
| 56 |
+
"min": 0,
|
| 57 |
+
"max": 0
|
| 58 |
+
},
|
| 59 |
+
"num_standby": {
|
| 60 |
+
"mean": 1.2,
|
| 61 |
+
"min": 0,
|
| 62 |
+
"max": 2
|
| 63 |
+
},
|
| 64 |
+
"num_maintenance": {
|
| 65 |
+
"mean": 0,
|
| 66 |
+
"min": 0,
|
| 67 |
+
"max": 0
|
| 68 |
+
},
|
| 69 |
+
"total_service_blocks": {
|
| 70 |
+
"mean": 10.4,
|
| 71 |
+
"min": 8,
|
| 72 |
+
"max": 14
|
| 73 |
+
}
|
| 74 |
+
}
|
| 75 |
+
},
|
| 76 |
+
{
|
| 77 |
+
"method": "ga",
|
| 78 |
+
"optimizer_family": "GreedyOptim",
|
| 79 |
+
"fleet_size": 10,
|
| 80 |
+
"num_runs": 5,
|
| 81 |
+
"successful_runs": 5,
|
| 82 |
+
"success_rate": "100.0%",
|
| 83 |
+
"execution_time": {
|
| 84 |
+
"min_seconds": 1.3963521669998045,
|
| 85 |
+
"max_seconds": 1.4883098969999082,
|
| 86 |
+
"mean_seconds": 1.4260893835998103,
|
| 87 |
+
"median_seconds": 1.4124550050000835,
|
| 88 |
+
"stdev_seconds": 0.03676729298817419
|
| 89 |
+
},
|
| 90 |
+
"optimization_scores": {
|
| 91 |
+
"mean": 8259.5625,
|
| 92 |
+
"min": 8259.5625,
|
| 93 |
+
"max": 8259.5625
|
| 94 |
+
}
|
| 95 |
+
},
|
| 96 |
+
{
|
| 97 |
+
"method": "pso",
|
| 98 |
+
"optimizer_family": "GreedyOptim",
|
| 99 |
+
"fleet_size": 10,
|
| 100 |
+
"num_runs": 5,
|
| 101 |
+
"successful_runs": 5,
|
| 102 |
+
"success_rate": "100.0%",
|
| 103 |
+
"execution_time": {
|
| 104 |
+
"min_seconds": 1.1987209819999407,
|
| 105 |
+
"max_seconds": 1.317151922999983,
|
| 106 |
+
"mean_seconds": 1.277441866800018,
|
| 107 |
+
"median_seconds": 1.3014346370000567,
|
| 108 |
+
"stdev_seconds": 0.049406619627260874
|
| 109 |
+
},
|
| 110 |
+
"optimization_scores": {
|
| 111 |
+
"mean": 8259.5625,
|
| 112 |
+
"min": 8259.5625,
|
| 113 |
+
"max": 8259.5625
|
| 114 |
+
}
|
| 115 |
+
},
|
| 116 |
+
{
|
| 117 |
+
"method": "cmaes",
|
| 118 |
+
"optimizer_family": "GreedyOptim",
|
| 119 |
+
"fleet_size": 10,
|
| 120 |
+
"num_runs": 5,
|
| 121 |
+
"successful_runs": 5,
|
| 122 |
+
"success_rate": "100.0%",
|
| 123 |
+
"execution_time": {
|
| 124 |
+
"min_seconds": 0.25710203799962983,
|
| 125 |
+
"max_seconds": 0.2891434930002106,
|
| 126 |
+
"mean_seconds": 0.27034687799996393,
|
| 127 |
+
"median_seconds": 0.26708701600000495,
|
| 128 |
+
"stdev_seconds": 0.01252247810000051
|
| 129 |
+
},
|
| 130 |
+
"optimization_scores": {
|
| 131 |
+
"mean": 8351.9026,
|
| 132 |
+
"min": 8259.5625,
|
| 133 |
+
"max": 8721.263
|
| 134 |
+
}
|
| 135 |
+
},
|
| 136 |
+
{
|
| 137 |
+
"optimizer": "MetroScheduleOptimizer",
|
| 138 |
+
"fleet_size": 15,
|
| 139 |
+
"num_stations": 22,
|
| 140 |
+
"num_runs": 5,
|
| 141 |
+
"successful_runs": 5,
|
| 142 |
+
"success_rate": "100.0%",
|
| 143 |
+
"execution_time": {
|
| 144 |
+
"min_seconds": 0.0005759119999311224,
|
| 145 |
+
"max_seconds": 0.0014056449999770848,
|
| 146 |
+
"mean_seconds": 0.0008308520000355202,
|
| 147 |
+
"median_seconds": 0.0006553590001203702,
|
| 148 |
+
"stdev_seconds": 0.00034941493942545516,
|
| 149 |
+
"all_runs_seconds": [
|
| 150 |
+
0.0014056449999770848,
|
| 151 |
+
0.0009195510001518414,
|
| 152 |
+
0.0006553590001203702,
|
| 153 |
+
0.000597792999997182,
|
| 154 |
+
0.0005759119999311224
|
| 155 |
+
]
|
| 156 |
+
},
|
| 157 |
+
"schedule_statistics": {
|
| 158 |
+
"num_trainsets": {
|
| 159 |
+
"mean": 15,
|
| 160 |
+
"min": 15,
|
| 161 |
+
"max": 15
|
| 162 |
+
},
|
| 163 |
+
"num_in_service": {
|
| 164 |
+
"mean": 0,
|
| 165 |
+
"min": 0,
|
| 166 |
+
"max": 0
|
| 167 |
+
},
|
| 168 |
+
"num_standby": {
|
| 169 |
+
"mean": 3.2,
|
| 170 |
+
"min": 0,
|
| 171 |
+
"max": 8
|
| 172 |
+
},
|
| 173 |
+
"num_maintenance": {
|
| 174 |
+
"mean": 0,
|
| 175 |
+
"min": 0,
|
| 176 |
+
"max": 0
|
| 177 |
+
},
|
| 178 |
+
"total_service_blocks": {
|
| 179 |
+
"mean": 14,
|
| 180 |
+
"min": 0,
|
| 181 |
+
"max": 24
|
| 182 |
+
}
|
| 183 |
+
}
|
| 184 |
+
},
|
| 185 |
+
{
|
| 186 |
+
"method": "ga",
|
| 187 |
+
"optimizer_family": "GreedyOptim",
|
| 188 |
+
"fleet_size": 15,
|
| 189 |
+
"num_runs": 5,
|
| 190 |
+
"successful_runs": 5,
|
| 191 |
+
"success_rate": "100.0%",
|
| 192 |
+
"execution_time": {
|
| 193 |
+
"min_seconds": 1.9778195669996421,
|
| 194 |
+
"max_seconds": 2.078312783000001,
|
| 195 |
+
"mean_seconds": 2.0281235989999913,
|
| 196 |
+
"median_seconds": 2.0414660280002863,
|
| 197 |
+
"stdev_seconds": 0.039198657499124455
|
| 198 |
+
},
|
| 199 |
+
"optimization_scores": {
|
| 200 |
+
"mean": 7221.458333333333,
|
| 201 |
+
"min": 7221.458333333333,
|
| 202 |
+
"max": 7221.458333333333
|
| 203 |
+
}
|
| 204 |
+
},
|
| 205 |
+
{
|
| 206 |
+
"method": "pso",
|
| 207 |
+
"optimizer_family": "GreedyOptim",
|
| 208 |
+
"fleet_size": 15,
|
| 209 |
+
"num_runs": 5,
|
| 210 |
+
"successful_runs": 5,
|
| 211 |
+
"success_rate": "100.0%",
|
| 212 |
+
"execution_time": {
|
| 213 |
+
"min_seconds": 1.3621163789998718,
|
| 214 |
+
"max_seconds": 1.4680279270000938,
|
| 215 |
+
"mean_seconds": 1.422008500999982,
|
| 216 |
+
"median_seconds": 1.43765709399986,
|
| 217 |
+
"stdev_seconds": 0.04642705859741245
|
| 218 |
+
},
|
| 219 |
+
"optimization_scores": {
|
| 220 |
+
"mean": 7718.583333333333,
|
| 221 |
+
"min": 7710.0,
|
| 222 |
+
"max": 7731.458333333333
|
| 223 |
+
}
|
| 224 |
+
},
|
| 225 |
+
{
|
| 226 |
+
"method": "cmaes",
|
| 227 |
+
"optimizer_family": "GreedyOptim",
|
| 228 |
+
"fleet_size": 15,
|
| 229 |
+
"num_runs": 5,
|
| 230 |
+
"successful_runs": 5,
|
| 231 |
+
"success_rate": "100.0%",
|
| 232 |
+
"execution_time": {
|
| 233 |
+
"min_seconds": 0.30759359300009237,
|
| 234 |
+
"max_seconds": 0.3274225889999798,
|
| 235 |
+
"mean_seconds": 0.31530408800008447,
|
| 236 |
+
"median_seconds": 0.31438662600021416,
|
| 237 |
+
"stdev_seconds": 0.0074082538027132995
|
| 238 |
+
},
|
| 239 |
+
"optimization_scores": {
|
| 240 |
+
"mean": 7471.166666666666,
|
| 241 |
+
"min": 7221.458333333333,
|
| 242 |
+
"max": 7731.458333333333
|
| 243 |
+
}
|
| 244 |
+
},
|
| 245 |
+
{
|
| 246 |
+
"optimizer": "MetroScheduleOptimizer",
|
| 247 |
+
"fleet_size": 20,
|
| 248 |
+
"num_stations": 22,
|
| 249 |
+
"num_runs": 5,
|
| 250 |
+
"successful_runs": 5,
|
| 251 |
+
"success_rate": "100.0%",
|
| 252 |
+
"execution_time": {
|
| 253 |
+
"min_seconds": 0.0010652710002432286,
|
| 254 |
+
"max_seconds": 0.0018792339997162344,
|
| 255 |
+
"mean_seconds": 0.0013811987999361008,
|
| 256 |
+
"median_seconds": 0.001251426999715477,
|
| 257 |
+
"stdev_seconds": 0.0003213910444020275,
|
| 258 |
+
"all_runs_seconds": [
|
| 259 |
+
0.0018792339997162344,
|
| 260 |
+
0.0010652710002432286,
|
| 261 |
+
0.001201556000069104,
|
| 262 |
+
0.001251426999715477,
|
| 263 |
+
0.0015085059999364603
|
| 264 |
+
]
|
| 265 |
+
},
|
| 266 |
+
"schedule_statistics": {
|
| 267 |
+
"num_trainsets": {
|
| 268 |
+
"mean": 20,
|
| 269 |
+
"min": 20,
|
| 270 |
+
"max": 20
|
| 271 |
+
},
|
| 272 |
+
"num_in_service": {
|
| 273 |
+
"mean": 0,
|
| 274 |
+
"min": 0,
|
| 275 |
+
"max": 0
|
| 276 |
+
},
|
| 277 |
+
"num_standby": {
|
| 278 |
+
"mean": 2.2,
|
| 279 |
+
"min": 1,
|
| 280 |
+
"max": 3
|
| 281 |
+
},
|
| 282 |
+
"num_maintenance": {
|
| 283 |
+
"mean": 0,
|
| 284 |
+
"min": 0,
|
| 285 |
+
"max": 0
|
| 286 |
+
},
|
| 287 |
+
"total_service_blocks": {
|
| 288 |
+
"mean": 18,
|
| 289 |
+
"min": 14,
|
| 290 |
+
"max": 20
|
| 291 |
+
}
|
| 292 |
+
}
|
| 293 |
+
},
|
| 294 |
+
{
|
| 295 |
+
"method": "ga",
|
| 296 |
+
"optimizer_family": "GreedyOptim",
|
| 297 |
+
"fleet_size": 20,
|
| 298 |
+
"num_runs": 5,
|
| 299 |
+
"successful_runs": 5,
|
| 300 |
+
"success_rate": "100.0%",
|
| 301 |
+
"execution_time": {
|
| 302 |
+
"min_seconds": 3.5978478239999276,
|
| 303 |
+
"max_seconds": 4.359904547000042,
|
| 304 |
+
"mean_seconds": 4.0308690387999375,
|
| 305 |
+
"median_seconds": 4.197869660999913,
|
| 306 |
+
"stdev_seconds": 0.3802696270167743
|
| 307 |
+
},
|
| 308 |
+
"optimization_scores": {
|
| 309 |
+
"mean": 7849.458531746031,
|
| 310 |
+
"min": 7316.736111111111,
|
| 311 |
+
"max": 8235.763888888889
|
| 312 |
+
}
|
| 313 |
+
},
|
| 314 |
+
{
|
| 315 |
+
"method": "pso",
|
| 316 |
+
"optimizer_family": "GreedyOptim",
|
| 317 |
+
"fleet_size": 20,
|
| 318 |
+
"num_runs": 5,
|
| 319 |
+
"successful_runs": 5,
|
| 320 |
+
"success_rate": "100.0%",
|
| 321 |
+
"execution_time": {
|
| 322 |
+
"min_seconds": 1.816424952000034,
|
| 323 |
+
"max_seconds": 2.2484812339998825,
|
| 324 |
+
"mean_seconds": 1.995718881799985,
|
| 325 |
+
"median_seconds": 1.963324914000168,
|
| 326 |
+
"stdev_seconds": 0.1625126481012335
|
| 327 |
+
},
|
| 328 |
+
"optimization_scores": {
|
| 329 |
+
"mean": 6039.215773809524,
|
| 330 |
+
"min": 5273.050595238095,
|
| 331 |
+
"max": 6704.523809523809
|
| 332 |
+
}
|
| 333 |
+
},
|
| 334 |
+
{
|
| 335 |
+
"method": "cmaes",
|
| 336 |
+
"optimizer_family": "GreedyOptim",
|
| 337 |
+
"fleet_size": 20,
|
| 338 |
+
"num_runs": 5,
|
| 339 |
+
"successful_runs": 5,
|
| 340 |
+
"success_rate": "100.0%",
|
| 341 |
+
"execution_time": {
|
| 342 |
+
"min_seconds": 0.3713683390001279,
|
| 343 |
+
"max_seconds": 0.42148448099987945,
|
| 344 |
+
"mean_seconds": 0.3929031652000958,
|
| 345 |
+
"median_seconds": 0.3885927700002867,
|
| 346 |
+
"stdev_seconds": 0.019848359739041352
|
| 347 |
+
},
|
| 348 |
+
"optimization_scores": {
|
| 349 |
+
"mean": 5718.677721088436,
|
| 350 |
+
"min": 5523.050595238095,
|
| 351 |
+
"max": 6013.050595238095
|
| 352 |
+
}
|
| 353 |
+
},
|
| 354 |
+
{
|
| 355 |
+
"optimizer": "MetroScheduleOptimizer",
|
| 356 |
+
"fleet_size": 25,
|
| 357 |
+
"num_stations": 22,
|
| 358 |
+
"num_runs": 5,
|
| 359 |
+
"successful_runs": 5,
|
| 360 |
+
"success_rate": "100.0%",
|
| 361 |
+
"execution_time": {
|
| 362 |
+
"min_seconds": 0.0011885819999406522,
|
| 363 |
+
"max_seconds": 0.006939703000170994,
|
| 364 |
+
"mean_seconds": 0.002958188200045697,
|
| 365 |
+
"median_seconds": 0.002493990999937523,
|
| 366 |
+
"stdev_seconds": 0.002311739115790229,
|
| 367 |
+
"all_runs_seconds": [
|
| 368 |
+
0.0026543580001998635,
|
| 369 |
+
0.006939703000170994,
|
| 370 |
+
0.002493990999937523,
|
| 371 |
+
0.0011885819999406522,
|
| 372 |
+
0.0015143069999794534
|
| 373 |
+
]
|
| 374 |
+
},
|
| 375 |
+
"schedule_statistics": {
|
| 376 |
+
"num_trainsets": {
|
| 377 |
+
"mean": 25,
|
| 378 |
+
"min": 25,
|
| 379 |
+
"max": 25
|
| 380 |
+
},
|
| 381 |
+
"num_in_service": {
|
| 382 |
+
"mean": 0,
|
| 383 |
+
"min": 0,
|
| 384 |
+
"max": 0
|
| 385 |
+
},
|
| 386 |
+
"num_standby": {
|
| 387 |
+
"mean": 4,
|
| 388 |
+
"min": 2,
|
| 389 |
+
"max": 5
|
| 390 |
+
},
|
| 391 |
+
"num_maintenance": {
|
| 392 |
+
"mean": 0,
|
| 393 |
+
"min": 0,
|
| 394 |
+
"max": 0
|
| 395 |
+
},
|
| 396 |
+
"total_service_blocks": {
|
| 397 |
+
"mean": 23.6,
|
| 398 |
+
"min": 18,
|
| 399 |
+
"max": 28
|
| 400 |
+
}
|
| 401 |
+
}
|
| 402 |
+
},
|
| 403 |
+
{
|
| 404 |
+
"method": "ga",
|
| 405 |
+
"optimizer_family": "GreedyOptim",
|
| 406 |
+
"fleet_size": 25,
|
| 407 |
+
"num_runs": 5,
|
| 408 |
+
"successful_runs": 5,
|
| 409 |
+
"success_rate": "100.0%",
|
| 410 |
+
"execution_time": {
|
| 411 |
+
"min_seconds": 4.450258830999701,
|
| 412 |
+
"max_seconds": 4.6299859079999806,
|
| 413 |
+
"mean_seconds": 4.527360460599903,
|
| 414 |
+
"median_seconds": 4.496251064999797,
|
| 415 |
+
"stdev_seconds": 0.07250204628025216
|
| 416 |
+
},
|
| 417 |
+
"optimization_scores": {
|
| 418 |
+
"mean": 7805.723412698413,
|
| 419 |
+
"min": 7205.519841269841,
|
| 420 |
+
"max": 8231.770833333334
|
| 421 |
+
}
|
| 422 |
+
},
|
| 423 |
+
{
|
| 424 |
+
"method": "pso",
|
| 425 |
+
"optimizer_family": "GreedyOptim",
|
| 426 |
+
"fleet_size": 25,
|
| 427 |
+
"num_runs": 5,
|
| 428 |
+
"successful_runs": 5,
|
| 429 |
+
"success_rate": "100.0%",
|
| 430 |
+
"execution_time": {
|
| 431 |
+
"min_seconds": 1.6021809260000737,
|
| 432 |
+
"max_seconds": 2.0892670769999313,
|
| 433 |
+
"mean_seconds": 1.9182770816000811,
|
| 434 |
+
"median_seconds": 1.934989805999976,
|
| 435 |
+
"stdev_seconds": 0.20000945840353515
|
| 436 |
+
},
|
| 437 |
+
"optimization_scores": {
|
| 438 |
+
"mean": 5416.512881562881,
|
| 439 |
+
"min": 4714.148148148148,
|
| 440 |
+
"max": 6194.027777777777
|
| 441 |
+
}
|
| 442 |
+
},
|
| 443 |
+
{
|
| 444 |
+
"method": "cmaes",
|
| 445 |
+
"optimizer_family": "GreedyOptim",
|
| 446 |
+
"fleet_size": 25,
|
| 447 |
+
"num_runs": 5,
|
| 448 |
+
"successful_runs": 5,
|
| 449 |
+
"success_rate": "100.0%",
|
| 450 |
+
"execution_time": {
|
| 451 |
+
"min_seconds": 0.37672309399977166,
|
| 452 |
+
"max_seconds": 0.4467183430001569,
|
| 453 |
+
"mean_seconds": 0.42155395619993213,
|
| 454 |
+
"median_seconds": 0.42633110800034046,
|
| 455 |
+
"stdev_seconds": 0.02678553643100765
|
| 456 |
+
},
|
| 457 |
+
"optimization_scores": {
|
| 458 |
+
"mean": 4805.237202380952,
|
| 459 |
+
"min": 4706.9484126984125,
|
| 460 |
+
"max": 5188.392361111111
|
| 461 |
+
}
|
| 462 |
+
},
|
| 463 |
+
{
|
| 464 |
+
"optimizer": "MetroScheduleOptimizer",
|
| 465 |
+
"fleet_size": 30,
|
| 466 |
+
"num_stations": 22,
|
| 467 |
+
"num_runs": 5,
|
| 468 |
+
"successful_runs": 5,
|
| 469 |
+
"success_rate": "100.0%",
|
| 470 |
+
"execution_time": {
|
| 471 |
+
"min_seconds": 0.0014640639997196558,
|
| 472 |
+
"max_seconds": 0.0024185390002458007,
|
| 473 |
+
"mean_seconds": 0.0020176196000647904,
|
| 474 |
+
"median_seconds": 0.002053440000054252,
|
| 475 |
+
"stdev_seconds": 0.0003518474507731157,
|
| 476 |
+
"all_runs_seconds": [
|
| 477 |
+
0.0024185390002458007,
|
| 478 |
+
0.0019762269998864213,
|
| 479 |
+
0.0014640639997196558,
|
| 480 |
+
0.002175828000417823,
|
| 481 |
+
0.002053440000054252
|
| 482 |
+
]
|
| 483 |
+
},
|
| 484 |
+
"schedule_statistics": {
|
| 485 |
+
"num_trainsets": {
|
| 486 |
+
"mean": 30,
|
| 487 |
+
"min": 30,
|
| 488 |
+
"max": 30
|
| 489 |
+
},
|
| 490 |
+
"num_in_service": {
|
| 491 |
+
"mean": 0,
|
| 492 |
+
"min": 0,
|
| 493 |
+
"max": 0
|
| 494 |
+
},
|
| 495 |
+
"num_standby": {
|
| 496 |
+
"mean": 3.4,
|
| 497 |
+
"min": 1,
|
| 498 |
+
"max": 6
|
| 499 |
+
},
|
| 500 |
+
"num_maintenance": {
|
| 501 |
+
"mean": 0,
|
| 502 |
+
"min": 0,
|
| 503 |
+
"max": 0
|
| 504 |
+
},
|
| 505 |
+
"total_service_blocks": {
|
| 506 |
+
"mean": 26,
|
| 507 |
+
"min": 22,
|
| 508 |
+
"max": 32
|
| 509 |
+
}
|
| 510 |
+
}
|
| 511 |
+
},
|
| 512 |
+
{
|
| 513 |
+
"method": "ga",
|
| 514 |
+
"optimizer_family": "GreedyOptim",
|
| 515 |
+
"fleet_size": 30,
|
| 516 |
+
"num_runs": 5,
|
| 517 |
+
"successful_runs": 5,
|
| 518 |
+
"success_rate": "100.0%",
|
| 519 |
+
"execution_time": {
|
| 520 |
+
"min_seconds": 3.656059525999808,
|
| 521 |
+
"max_seconds": 3.9020974959998966,
|
| 522 |
+
"mean_seconds": 3.7467717739999897,
|
| 523 |
+
"median_seconds": 3.702346977000161,
|
| 524 |
+
"stdev_seconds": 0.10654896202242645
|
| 525 |
+
},
|
| 526 |
+
"optimization_scores": {
|
| 527 |
+
"mean": 5565.882416194916,
|
| 528 |
+
"min": 5565.882416194916,
|
| 529 |
+
"max": 5565.882416194916
|
| 530 |
+
}
|
| 531 |
+
},
|
| 532 |
+
{
|
| 533 |
+
"method": "pso",
|
| 534 |
+
"optimizer_family": "GreedyOptim",
|
| 535 |
+
"fleet_size": 30,
|
| 536 |
+
"num_runs": 5,
|
| 537 |
+
"successful_runs": 5,
|
| 538 |
+
"success_rate": "100.0%",
|
| 539 |
+
"execution_time": {
|
| 540 |
+
"min_seconds": 1.9881061810001484,
|
| 541 |
+
"max_seconds": 2.4008741109996663,
|
| 542 |
+
"mean_seconds": 2.1433096785999624,
|
| 543 |
+
"median_seconds": 2.1254254129999026,
|
| 544 |
+
"stdev_seconds": 0.16246516059375193
|
| 545 |
+
},
|
| 546 |
+
"optimization_scores": {
|
| 547 |
+
"mean": 4656.59065934066,
|
| 548 |
+
"min": 4138.327838827839,
|
| 549 |
+
"max": 5162.010073260073
|
| 550 |
+
}
|
| 551 |
+
},
|
| 552 |
+
{
|
| 553 |
+
"method": "cmaes",
|
| 554 |
+
"optimizer_family": "GreedyOptim",
|
| 555 |
+
"fleet_size": 30,
|
| 556 |
+
"num_runs": 5,
|
| 557 |
+
"successful_runs": 5,
|
| 558 |
+
"success_rate": "100.0%",
|
| 559 |
+
"execution_time": {
|
| 560 |
+
"min_seconds": 0.5601028310002221,
|
| 561 |
+
"max_seconds": 0.5995334710000861,
|
| 562 |
+
"mean_seconds": 0.5794001551999827,
|
| 563 |
+
"median_seconds": 0.5759605129996999,
|
| 564 |
+
"stdev_seconds": 0.017854577483330164
|
| 565 |
+
},
|
| 566 |
+
"optimization_scores": {
|
| 567 |
+
"mean": 3226.4587468087466,
|
| 568 |
+
"min": 3128.286172161172,
|
| 569 |
+
"max": 3619.1490453990455
|
| 570 |
+
}
|
| 571 |
+
},
|
| 572 |
+
{
|
| 573 |
+
"optimizer": "MetroScheduleOptimizer",
|
| 574 |
+
"fleet_size": 40,
|
| 575 |
+
"num_stations": 22,
|
| 576 |
+
"num_runs": 5,
|
| 577 |
+
"successful_runs": 5,
|
| 578 |
+
"success_rate": "100.0%",
|
| 579 |
+
"execution_time": {
|
| 580 |
+
"min_seconds": 0.001798295999833499,
|
| 581 |
+
"max_seconds": 0.004733248999855277,
|
| 582 |
+
"mean_seconds": 0.003044102399962867,
|
| 583 |
+
"median_seconds": 0.0031079119999049,
|
| 584 |
+
"stdev_seconds": 0.0012054221276706066,
|
| 585 |
+
"all_runs_seconds": [
|
| 586 |
+
0.0031079119999049,
|
| 587 |
+
0.001992327000152727,
|
| 588 |
+
0.001798295999833499,
|
| 589 |
+
0.003588728000067931,
|
| 590 |
+
0.004733248999855277
|
| 591 |
+
]
|
| 592 |
+
},
|
| 593 |
+
"schedule_statistics": {
|
| 594 |
+
"num_trainsets": {
|
| 595 |
+
"mean": 40,
|
| 596 |
+
"min": 40,
|
| 597 |
+
"max": 40
|
| 598 |
+
},
|
| 599 |
+
"num_in_service": {
|
| 600 |
+
"mean": 0,
|
| 601 |
+
"min": 0,
|
| 602 |
+
"max": 0
|
| 603 |
+
},
|
| 604 |
+
"num_standby": {
|
| 605 |
+
"mean": 5.6,
|
| 606 |
+
"min": 4,
|
| 607 |
+
"max": 7
|
| 608 |
+
},
|
| 609 |
+
"num_maintenance": {
|
| 610 |
+
"mean": 0,
|
| 611 |
+
"min": 0,
|
| 612 |
+
"max": 0
|
| 613 |
+
},
|
| 614 |
+
"total_service_blocks": {
|
| 615 |
+
"mean": 27.6,
|
| 616 |
+
"min": 20,
|
| 617 |
+
"max": 30
|
| 618 |
+
}
|
| 619 |
+
}
|
| 620 |
+
},
|
| 621 |
+
{
|
| 622 |
+
"method": "ga",
|
| 623 |
+
"optimizer_family": "GreedyOptim",
|
| 624 |
+
"fleet_size": 40,
|
| 625 |
+
"num_runs": 5,
|
| 626 |
+
"successful_runs": 5,
|
| 627 |
+
"success_rate": "100.0%",
|
| 628 |
+
"execution_time": {
|
| 629 |
+
"min_seconds": 4.770303608999711,
|
| 630 |
+
"max_seconds": 4.999881856000229,
|
| 631 |
+
"mean_seconds": 4.887823572599973,
|
| 632 |
+
"median_seconds": 4.915720539000176,
|
| 633 |
+
"stdev_seconds": 0.10805978619659268
|
| 634 |
+
},
|
| 635 |
+
"optimization_scores": {
|
| 636 |
+
"mean": 4668.188604324968,
|
| 637 |
+
"min": 4668.188604324968,
|
| 638 |
+
"max": 4668.188604324968
|
| 639 |
+
}
|
| 640 |
+
},
|
| 641 |
+
{
|
| 642 |
+
"method": "pso",
|
| 643 |
+
"optimizer_family": "GreedyOptim",
|
| 644 |
+
"fleet_size": 40,
|
| 645 |
+
"num_runs": 5,
|
| 646 |
+
"successful_runs": 5,
|
| 647 |
+
"success_rate": "100.0%",
|
| 648 |
+
"execution_time": {
|
| 649 |
+
"min_seconds": 2.324307186999704,
|
| 650 |
+
"max_seconds": 3.1814292060003027,
|
| 651 |
+
"mean_seconds": 2.920363885200004,
|
| 652 |
+
"median_seconds": 3.0396999100003086,
|
| 653 |
+
"stdev_seconds": 0.3411733712592887
|
| 654 |
+
},
|
| 655 |
+
"optimization_scores": {
|
| 656 |
+
"mean": 4206.6411169518315,
|
| 657 |
+
"min": 3223.605957534529,
|
| 658 |
+
"max": 5240.704365079365
|
| 659 |
+
}
|
| 660 |
+
},
|
| 661 |
+
{
|
| 662 |
+
"method": "cmaes",
|
| 663 |
+
"optimizer_family": "GreedyOptim",
|
| 664 |
+
"fleet_size": 40,
|
| 665 |
+
"num_runs": 5,
|
| 666 |
+
"successful_runs": 5,
|
| 667 |
+
"success_rate": "100.0%",
|
| 668 |
+
"execution_time": {
|
| 669 |
+
"min_seconds": 0.8600020419999055,
|
| 670 |
+
"max_seconds": 1.1148396250000587,
|
| 671 |
+
"mean_seconds": 0.9417086120000022,
|
| 672 |
+
"median_seconds": 0.9037215389998892,
|
| 673 |
+
"stdev_seconds": 0.10007564261354274
|
| 674 |
+
},
|
| 675 |
+
"optimization_scores": {
|
| 676 |
+
"mean": 2723.605957534529,
|
| 677 |
+
"min": 2723.605957534529,
|
| 678 |
+
"max": 2723.605957534529
|
| 679 |
+
}
|
| 680 |
+
}
|
| 681 |
+
],
|
| 682 |
+
"summary": {
|
| 683 |
+
"by_fleet_size": {
|
| 684 |
+
"10": [
|
| 685 |
+
{
|
| 686 |
+
"optimizer": "MetroScheduleOptimizer",
|
| 687 |
+
"mean_time_seconds": 0.0004975758000909991,
|
| 688 |
+
"success_rate": "100.0%"
|
| 689 |
+
},
|
| 690 |
+
{
|
| 691 |
+
"optimizer": "ga",
|
| 692 |
+
"mean_time_seconds": 1.4260893835998103,
|
| 693 |
+
"success_rate": "100.0%"
|
| 694 |
+
},
|
| 695 |
+
{
|
| 696 |
+
"optimizer": "pso",
|
| 697 |
+
"mean_time_seconds": 1.277441866800018,
|
| 698 |
+
"success_rate": "100.0%"
|
| 699 |
+
},
|
| 700 |
+
{
|
| 701 |
+
"optimizer": "cmaes",
|
| 702 |
+
"mean_time_seconds": 0.27034687799996393,
|
| 703 |
+
"success_rate": "100.0%"
|
| 704 |
+
}
|
| 705 |
+
],
|
| 706 |
+
"15": [
|
| 707 |
+
{
|
| 708 |
+
"optimizer": "MetroScheduleOptimizer",
|
| 709 |
+
"mean_time_seconds": 0.0008308520000355202,
|
| 710 |
+
"success_rate": "100.0%"
|
| 711 |
+
},
|
| 712 |
+
{
|
| 713 |
+
"optimizer": "ga",
|
| 714 |
+
"mean_time_seconds": 2.0281235989999913,
|
| 715 |
+
"success_rate": "100.0%"
|
| 716 |
+
},
|
| 717 |
+
{
|
| 718 |
+
"optimizer": "pso",
|
| 719 |
+
"mean_time_seconds": 1.422008500999982,
|
| 720 |
+
"success_rate": "100.0%"
|
| 721 |
+
},
|
| 722 |
+
{
|
| 723 |
+
"optimizer": "cmaes",
|
| 724 |
+
"mean_time_seconds": 0.31530408800008447,
|
| 725 |
+
"success_rate": "100.0%"
|
| 726 |
+
}
|
| 727 |
+
],
|
| 728 |
+
"20": [
|
| 729 |
+
{
|
| 730 |
+
"optimizer": "MetroScheduleOptimizer",
|
| 731 |
+
"mean_time_seconds": 0.0013811987999361008,
|
| 732 |
+
"success_rate": "100.0%"
|
| 733 |
+
},
|
| 734 |
+
{
|
| 735 |
+
"optimizer": "ga",
|
| 736 |
+
"mean_time_seconds": 4.0308690387999375,
|
| 737 |
+
"success_rate": "100.0%"
|
| 738 |
+
},
|
| 739 |
+
{
|
| 740 |
+
"optimizer": "pso",
|
| 741 |
+
"mean_time_seconds": 1.995718881799985,
|
| 742 |
+
"success_rate": "100.0%"
|
| 743 |
+
},
|
| 744 |
+
{
|
| 745 |
+
"optimizer": "cmaes",
|
| 746 |
+
"mean_time_seconds": 0.3929031652000958,
|
| 747 |
+
"success_rate": "100.0%"
|
| 748 |
+
}
|
| 749 |
+
],
|
| 750 |
+
"25": [
|
| 751 |
+
{
|
| 752 |
+
"optimizer": "MetroScheduleOptimizer",
|
| 753 |
+
"mean_time_seconds": 0.002958188200045697,
|
| 754 |
+
"success_rate": "100.0%"
|
| 755 |
+
},
|
| 756 |
+
{
|
| 757 |
+
"optimizer": "ga",
|
| 758 |
+
"mean_time_seconds": 4.527360460599903,
|
| 759 |
+
"success_rate": "100.0%"
|
| 760 |
+
},
|
| 761 |
+
{
|
| 762 |
+
"optimizer": "pso",
|
| 763 |
+
"mean_time_seconds": 1.9182770816000811,
|
| 764 |
+
"success_rate": "100.0%"
|
| 765 |
+
},
|
| 766 |
+
{
|
| 767 |
+
"optimizer": "cmaes",
|
| 768 |
+
"mean_time_seconds": 0.42155395619993213,
|
| 769 |
+
"success_rate": "100.0%"
|
| 770 |
+
}
|
| 771 |
+
],
|
| 772 |
+
"30": [
|
| 773 |
+
{
|
| 774 |
+
"optimizer": "MetroScheduleOptimizer",
|
| 775 |
+
"mean_time_seconds": 0.0020176196000647904,
|
| 776 |
+
"success_rate": "100.0%"
|
| 777 |
+
},
|
| 778 |
+
{
|
| 779 |
+
"optimizer": "ga",
|
| 780 |
+
"mean_time_seconds": 3.7467717739999897,
|
| 781 |
+
"success_rate": "100.0%"
|
| 782 |
+
},
|
| 783 |
+
{
|
| 784 |
+
"optimizer": "pso",
|
| 785 |
+
"mean_time_seconds": 2.1433096785999624,
|
| 786 |
+
"success_rate": "100.0%"
|
| 787 |
+
},
|
| 788 |
+
{
|
| 789 |
+
"optimizer": "cmaes",
|
| 790 |
+
"mean_time_seconds": 0.5794001551999827,
|
| 791 |
+
"success_rate": "100.0%"
|
| 792 |
+
}
|
| 793 |
+
],
|
| 794 |
+
"40": [
|
| 795 |
+
{
|
| 796 |
+
"optimizer": "MetroScheduleOptimizer",
|
| 797 |
+
"mean_time_seconds": 0.003044102399962867,
|
| 798 |
+
"success_rate": "100.0%"
|
| 799 |
+
},
|
| 800 |
+
{
|
| 801 |
+
"optimizer": "ga",
|
| 802 |
+
"mean_time_seconds": 4.887823572599973,
|
| 803 |
+
"success_rate": "100.0%"
|
| 804 |
+
},
|
| 805 |
+
{
|
| 806 |
+
"optimizer": "pso",
|
| 807 |
+
"mean_time_seconds": 2.920363885200004,
|
| 808 |
+
"success_rate": "100.0%"
|
| 809 |
+
},
|
| 810 |
+
{
|
| 811 |
+
"optimizer": "cmaes",
|
| 812 |
+
"mean_time_seconds": 0.9417086120000022,
|
| 813 |
+
"success_rate": "100.0%"
|
| 814 |
+
}
|
| 815 |
+
]
|
| 816 |
+
},
|
| 817 |
+
"overall_rankings": {
|
| 818 |
+
"MetroScheduleOptimizer": {
|
| 819 |
+
"rank": 1,
|
| 820 |
+
"avg_time_seconds": 0.0017882561333559957
|
| 821 |
+
},
|
| 822 |
+
"cmaes": {
|
| 823 |
+
"rank": 2,
|
| 824 |
+
"avg_time_seconds": 0.4868694757666769
|
| 825 |
+
},
|
| 826 |
+
"pso": {
|
| 827 |
+
"rank": 3,
|
| 828 |
+
"avg_time_seconds": 1.9461866491666722
|
| 829 |
+
},
|
| 830 |
+
"ga": {
|
| 831 |
+
"rank": 4,
|
| 832 |
+
"avg_time_seconds": 3.4411729714332675
|
| 833 |
+
}
|
| 834 |
+
},
|
| 835 |
+
"fastest_optimizer": "MetroScheduleOptimizer",
|
| 836 |
+
"fastest_time_seconds": 0.0017882561333559957
|
| 837 |
+
}
|
| 838 |
+
}
|
schedule_performance_report_20251106_134014.txt
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
================================================================================
|
| 2 |
+
METRO SCHEDULE GENERATION PERFORMANCE REPORT
|
| 3 |
+
================================================================================
|
| 4 |
+
|
| 5 |
+
Generated: 2025-11-06 13:40:14
|
| 6 |
+
Test Type: Schedule Generation Time & Computational Efficiency
|
| 7 |
+
|
| 8 |
+
EXECUTIVE SUMMARY
|
| 9 |
+
--------------------------------------------------------------------------------
|
| 10 |
+
|
| 11 |
+
Fastest Optimizer: MetroScheduleOptimizer
|
| 12 |
+
Best Average Time: 0.0018 seconds
|
| 13 |
+
|
| 14 |
+
Overall Performance Rankings:
|
| 15 |
+
1. MetroScheduleOptimizer: 0.0018s
|
| 16 |
+
2. cmaes: 0.4869s
|
| 17 |
+
3. pso: 1.9462s
|
| 18 |
+
4. ga: 3.4412s
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
DETAILED RESULTS
|
| 22 |
+
--------------------------------------------------------------------------------
|
| 23 |
+
|
| 24 |
+
Optimizer/Method: MetroScheduleOptimizer
|
| 25 |
+
Fleet Size: 10 trains
|
| 26 |
+
Success Rate: 100.0%
|
| 27 |
+
Execution Time Statistics:
|
| 28 |
+
Mean: 0.0005s
|
| 29 |
+
Median: 0.0005s
|
| 30 |
+
Min: 0.0004s
|
| 31 |
+
Max: 0.0007s
|
| 32 |
+
StdDev: 0.0001s
|
| 33 |
+
|
| 34 |
+
--------------------------------------------------------------------------------
|
| 35 |
+
|
| 36 |
+
Optimizer/Method: ga
|
| 37 |
+
Fleet Size: 10 trains
|
| 38 |
+
Success Rate: 100.0%
|
| 39 |
+
Execution Time Statistics:
|
| 40 |
+
Mean: 1.4261s
|
| 41 |
+
Median: 1.4125s
|
| 42 |
+
Min: 1.3964s
|
| 43 |
+
Max: 1.4883s
|
| 44 |
+
StdDev: 0.0368s
|
| 45 |
+
Optimization Scores:
|
| 46 |
+
Mean: 8259.5625
|
| 47 |
+
Min: 8259.5625
|
| 48 |
+
Max: 8259.5625
|
| 49 |
+
|
| 50 |
+
--------------------------------------------------------------------------------
|
| 51 |
+
|
| 52 |
+
Optimizer/Method: pso
|
| 53 |
+
Fleet Size: 10 trains
|
| 54 |
+
Success Rate: 100.0%
|
| 55 |
+
Execution Time Statistics:
|
| 56 |
+
Mean: 1.2774s
|
| 57 |
+
Median: 1.3014s
|
| 58 |
+
Min: 1.1987s
|
| 59 |
+
Max: 1.3172s
|
| 60 |
+
StdDev: 0.0494s
|
| 61 |
+
Optimization Scores:
|
| 62 |
+
Mean: 8259.5625
|
| 63 |
+
Min: 8259.5625
|
| 64 |
+
Max: 8259.5625
|
| 65 |
+
|
| 66 |
+
--------------------------------------------------------------------------------
|
| 67 |
+
|
| 68 |
+
Optimizer/Method: cmaes
|
| 69 |
+
Fleet Size: 10 trains
|
| 70 |
+
Success Rate: 100.0%
|
| 71 |
+
Execution Time Statistics:
|
| 72 |
+
Mean: 0.2703s
|
| 73 |
+
Median: 0.2671s
|
| 74 |
+
Min: 0.2571s
|
| 75 |
+
Max: 0.2891s
|
| 76 |
+
StdDev: 0.0125s
|
| 77 |
+
Optimization Scores:
|
| 78 |
+
Mean: 8351.9026
|
| 79 |
+
Min: 8259.5625
|
| 80 |
+
Max: 8721.2630
|
| 81 |
+
|
| 82 |
+
--------------------------------------------------------------------------------
|
| 83 |
+
|
| 84 |
+
Optimizer/Method: MetroScheduleOptimizer
|
| 85 |
+
Fleet Size: 15 trains
|
| 86 |
+
Success Rate: 100.0%
|
| 87 |
+
Execution Time Statistics:
|
| 88 |
+
Mean: 0.0008s
|
| 89 |
+
Median: 0.0007s
|
| 90 |
+
Min: 0.0006s
|
| 91 |
+
Max: 0.0014s
|
| 92 |
+
StdDev: 0.0003s
|
| 93 |
+
|
| 94 |
+
--------------------------------------------------------------------------------
|
| 95 |
+
|
| 96 |
+
Optimizer/Method: ga
|
| 97 |
+
Fleet Size: 15 trains
|
| 98 |
+
Success Rate: 100.0%
|
| 99 |
+
Execution Time Statistics:
|
| 100 |
+
Mean: 2.0281s
|
| 101 |
+
Median: 2.0415s
|
| 102 |
+
Min: 1.9778s
|
| 103 |
+
Max: 2.0783s
|
| 104 |
+
StdDev: 0.0392s
|
| 105 |
+
Optimization Scores:
|
| 106 |
+
Mean: 7221.4583
|
| 107 |
+
Min: 7221.4583
|
| 108 |
+
Max: 7221.4583
|
| 109 |
+
|
| 110 |
+
--------------------------------------------------------------------------------
|
| 111 |
+
|
| 112 |
+
Optimizer/Method: pso
|
| 113 |
+
Fleet Size: 15 trains
|
| 114 |
+
Success Rate: 100.0%
|
| 115 |
+
Execution Time Statistics:
|
| 116 |
+
Mean: 1.4220s
|
| 117 |
+
Median: 1.4377s
|
| 118 |
+
Min: 1.3621s
|
| 119 |
+
Max: 1.4680s
|
| 120 |
+
StdDev: 0.0464s
|
| 121 |
+
Optimization Scores:
|
| 122 |
+
Mean: 7718.5833
|
| 123 |
+
Min: 7710.0000
|
| 124 |
+
Max: 7731.4583
|
| 125 |
+
|
| 126 |
+
--------------------------------------------------------------------------------
|
| 127 |
+
|
| 128 |
+
Optimizer/Method: cmaes
|
| 129 |
+
Fleet Size: 15 trains
|
| 130 |
+
Success Rate: 100.0%
|
| 131 |
+
Execution Time Statistics:
|
| 132 |
+
Mean: 0.3153s
|
| 133 |
+
Median: 0.3144s
|
| 134 |
+
Min: 0.3076s
|
| 135 |
+
Max: 0.3274s
|
| 136 |
+
StdDev: 0.0074s
|
| 137 |
+
Optimization Scores:
|
| 138 |
+
Mean: 7471.1667
|
| 139 |
+
Min: 7221.4583
|
| 140 |
+
Max: 7731.4583
|
| 141 |
+
|
| 142 |
+
--------------------------------------------------------------------------------
|
| 143 |
+
|
| 144 |
+
Optimizer/Method: MetroScheduleOptimizer
|
| 145 |
+
Fleet Size: 20 trains
|
| 146 |
+
Success Rate: 100.0%
|
| 147 |
+
Execution Time Statistics:
|
| 148 |
+
Mean: 0.0014s
|
| 149 |
+
Median: 0.0013s
|
| 150 |
+
Min: 0.0011s
|
| 151 |
+
Max: 0.0019s
|
| 152 |
+
StdDev: 0.0003s
|
| 153 |
+
|
| 154 |
+
--------------------------------------------------------------------------------
|
| 155 |
+
|
| 156 |
+
Optimizer/Method: ga
|
| 157 |
+
Fleet Size: 20 trains
|
| 158 |
+
Success Rate: 100.0%
|
| 159 |
+
Execution Time Statistics:
|
| 160 |
+
Mean: 4.0309s
|
| 161 |
+
Median: 4.1979s
|
| 162 |
+
Min: 3.5978s
|
| 163 |
+
Max: 4.3599s
|
| 164 |
+
StdDev: 0.3803s
|
| 165 |
+
Optimization Scores:
|
| 166 |
+
Mean: 7849.4585
|
| 167 |
+
Min: 7316.7361
|
| 168 |
+
Max: 8235.7639
|
| 169 |
+
|
| 170 |
+
--------------------------------------------------------------------------------
|
| 171 |
+
|
| 172 |
+
Optimizer/Method: pso
|
| 173 |
+
Fleet Size: 20 trains
|
| 174 |
+
Success Rate: 100.0%
|
| 175 |
+
Execution Time Statistics:
|
| 176 |
+
Mean: 1.9957s
|
| 177 |
+
Median: 1.9633s
|
| 178 |
+
Min: 1.8164s
|
| 179 |
+
Max: 2.2485s
|
| 180 |
+
StdDev: 0.1625s
|
| 181 |
+
Optimization Scores:
|
| 182 |
+
Mean: 6039.2158
|
| 183 |
+
Min: 5273.0506
|
| 184 |
+
Max: 6704.5238
|
| 185 |
+
|
| 186 |
+
--------------------------------------------------------------------------------
|
| 187 |
+
|
| 188 |
+
Optimizer/Method: cmaes
|
| 189 |
+
Fleet Size: 20 trains
|
| 190 |
+
Success Rate: 100.0%
|
| 191 |
+
Execution Time Statistics:
|
| 192 |
+
Mean: 0.3929s
|
| 193 |
+
Median: 0.3886s
|
| 194 |
+
Min: 0.3714s
|
| 195 |
+
Max: 0.4215s
|
| 196 |
+
StdDev: 0.0198s
|
| 197 |
+
Optimization Scores:
|
| 198 |
+
Mean: 5718.6777
|
| 199 |
+
Min: 5523.0506
|
| 200 |
+
Max: 6013.0506
|
| 201 |
+
|
| 202 |
+
--------------------------------------------------------------------------------
|
| 203 |
+
|
| 204 |
+
Optimizer/Method: MetroScheduleOptimizer
|
| 205 |
+
Fleet Size: 25 trains
|
| 206 |
+
Success Rate: 100.0%
|
| 207 |
+
Execution Time Statistics:
|
| 208 |
+
Mean: 0.0030s
|
| 209 |
+
Median: 0.0025s
|
| 210 |
+
Min: 0.0012s
|
| 211 |
+
Max: 0.0069s
|
| 212 |
+
StdDev: 0.0023s
|
| 213 |
+
|
| 214 |
+
--------------------------------------------------------------------------------
|
| 215 |
+
|
| 216 |
+
Optimizer/Method: ga
|
| 217 |
+
Fleet Size: 25 trains
|
| 218 |
+
Success Rate: 100.0%
|
| 219 |
+
Execution Time Statistics:
|
| 220 |
+
Mean: 4.5274s
|
| 221 |
+
Median: 4.4963s
|
| 222 |
+
Min: 4.4503s
|
| 223 |
+
Max: 4.6300s
|
| 224 |
+
StdDev: 0.0725s
|
| 225 |
+
Optimization Scores:
|
| 226 |
+
Mean: 7805.7234
|
| 227 |
+
Min: 7205.5198
|
| 228 |
+
Max: 8231.7708
|
| 229 |
+
|
| 230 |
+
--------------------------------------------------------------------------------
|
| 231 |
+
|
| 232 |
+
Optimizer/Method: pso
|
| 233 |
+
Fleet Size: 25 trains
|
| 234 |
+
Success Rate: 100.0%
|
| 235 |
+
Execution Time Statistics:
|
| 236 |
+
Mean: 1.9183s
|
| 237 |
+
Median: 1.9350s
|
| 238 |
+
Min: 1.6022s
|
| 239 |
+
Max: 2.0893s
|
| 240 |
+
StdDev: 0.2000s
|
| 241 |
+
Optimization Scores:
|
| 242 |
+
Mean: 5416.5129
|
| 243 |
+
Min: 4714.1481
|
| 244 |
+
Max: 6194.0278
|
| 245 |
+
|
| 246 |
+
--------------------------------------------------------------------------------
|
| 247 |
+
|
| 248 |
+
Optimizer/Method: cmaes
|
| 249 |
+
Fleet Size: 25 trains
|
| 250 |
+
Success Rate: 100.0%
|
| 251 |
+
Execution Time Statistics:
|
| 252 |
+
Mean: 0.4216s
|
| 253 |
+
Median: 0.4263s
|
| 254 |
+
Min: 0.3767s
|
| 255 |
+
Max: 0.4467s
|
| 256 |
+
StdDev: 0.0268s
|
| 257 |
+
Optimization Scores:
|
| 258 |
+
Mean: 4805.2372
|
| 259 |
+
Min: 4706.9484
|
| 260 |
+
Max: 5188.3924
|
| 261 |
+
|
| 262 |
+
--------------------------------------------------------------------------------
|
| 263 |
+
|
| 264 |
+
Optimizer/Method: MetroScheduleOptimizer
|
| 265 |
+
Fleet Size: 30 trains
|
| 266 |
+
Success Rate: 100.0%
|
| 267 |
+
Execution Time Statistics:
|
| 268 |
+
Mean: 0.0020s
|
| 269 |
+
Median: 0.0021s
|
| 270 |
+
Min: 0.0015s
|
| 271 |
+
Max: 0.0024s
|
| 272 |
+
StdDev: 0.0004s
|
| 273 |
+
|
| 274 |
+
--------------------------------------------------------------------------------
|
| 275 |
+
|
| 276 |
+
Optimizer/Method: ga
|
| 277 |
+
Fleet Size: 30 trains
|
| 278 |
+
Success Rate: 100.0%
|
| 279 |
+
Execution Time Statistics:
|
| 280 |
+
Mean: 3.7468s
|
| 281 |
+
Median: 3.7023s
|
| 282 |
+
Min: 3.6561s
|
| 283 |
+
Max: 3.9021s
|
| 284 |
+
StdDev: 0.1065s
|
| 285 |
+
Optimization Scores:
|
| 286 |
+
Mean: 5565.8824
|
| 287 |
+
Min: 5565.8824
|
| 288 |
+
Max: 5565.8824
|
| 289 |
+
|
| 290 |
+
--------------------------------------------------------------------------------
|
| 291 |
+
|
| 292 |
+
Optimizer/Method: pso
|
| 293 |
+
Fleet Size: 30 trains
|
| 294 |
+
Success Rate: 100.0%
|
| 295 |
+
Execution Time Statistics:
|
| 296 |
+
Mean: 2.1433s
|
| 297 |
+
Median: 2.1254s
|
| 298 |
+
Min: 1.9881s
|
| 299 |
+
Max: 2.4009s
|
| 300 |
+
StdDev: 0.1625s
|
| 301 |
+
Optimization Scores:
|
| 302 |
+
Mean: 4656.5907
|
| 303 |
+
Min: 4138.3278
|
| 304 |
+
Max: 5162.0101
|
| 305 |
+
|
| 306 |
+
--------------------------------------------------------------------------------
|
| 307 |
+
|
| 308 |
+
Optimizer/Method: cmaes
|
| 309 |
+
Fleet Size: 30 trains
|
| 310 |
+
Success Rate: 100.0%
|
| 311 |
+
Execution Time Statistics:
|
| 312 |
+
Mean: 0.5794s
|
| 313 |
+
Median: 0.5760s
|
| 314 |
+
Min: 0.5601s
|
| 315 |
+
Max: 0.5995s
|
| 316 |
+
StdDev: 0.0179s
|
| 317 |
+
Optimization Scores:
|
| 318 |
+
Mean: 3226.4587
|
| 319 |
+
Min: 3128.2862
|
| 320 |
+
Max: 3619.1490
|
| 321 |
+
|
| 322 |
+
--------------------------------------------------------------------------------
|
| 323 |
+
|
| 324 |
+
Optimizer/Method: MetroScheduleOptimizer
|
| 325 |
+
Fleet Size: 40 trains
|
| 326 |
+
Success Rate: 100.0%
|
| 327 |
+
Execution Time Statistics:
|
| 328 |
+
Mean: 0.0030s
|
| 329 |
+
Median: 0.0031s
|
| 330 |
+
Min: 0.0018s
|
| 331 |
+
Max: 0.0047s
|
| 332 |
+
StdDev: 0.0012s
|
| 333 |
+
|
| 334 |
+
--------------------------------------------------------------------------------
|
| 335 |
+
|
| 336 |
+
Optimizer/Method: ga
|
| 337 |
+
Fleet Size: 40 trains
|
| 338 |
+
Success Rate: 100.0%
|
| 339 |
+
Execution Time Statistics:
|
| 340 |
+
Mean: 4.8878s
|
| 341 |
+
Median: 4.9157s
|
| 342 |
+
Min: 4.7703s
|
| 343 |
+
Max: 4.9999s
|
| 344 |
+
StdDev: 0.1081s
|
| 345 |
+
Optimization Scores:
|
| 346 |
+
Mean: 4668.1886
|
| 347 |
+
Min: 4668.1886
|
| 348 |
+
Max: 4668.1886
|
| 349 |
+
|
| 350 |
+
--------------------------------------------------------------------------------
|
| 351 |
+
|
| 352 |
+
Optimizer/Method: pso
|
| 353 |
+
Fleet Size: 40 trains
|
| 354 |
+
Success Rate: 100.0%
|
| 355 |
+
Execution Time Statistics:
|
| 356 |
+
Mean: 2.9204s
|
| 357 |
+
Median: 3.0397s
|
| 358 |
+
Min: 2.3243s
|
| 359 |
+
Max: 3.1814s
|
| 360 |
+
StdDev: 0.3412s
|
| 361 |
+
Optimization Scores:
|
| 362 |
+
Mean: 4206.6411
|
| 363 |
+
Min: 3223.6060
|
| 364 |
+
Max: 5240.7044
|
| 365 |
+
|
| 366 |
+
--------------------------------------------------------------------------------
|
| 367 |
+
|
| 368 |
+
Optimizer/Method: cmaes
|
| 369 |
+
Fleet Size: 40 trains
|
| 370 |
+
Success Rate: 100.0%
|
| 371 |
+
Execution Time Statistics:
|
| 372 |
+
Mean: 0.9417s
|
| 373 |
+
Median: 0.9037s
|
| 374 |
+
Min: 0.8600s
|
| 375 |
+
Max: 1.1148s
|
| 376 |
+
StdDev: 0.1001s
|
| 377 |
+
Optimization Scores:
|
| 378 |
+
Mean: 2723.6060
|
| 379 |
+
Min: 2723.6060
|
| 380 |
+
Max: 2723.6060
|
| 381 |
+
|
| 382 |
+
--------------------------------------------------------------------------------
|
| 383 |
+
|