quantrs2_anneal/applications/
transportation.rs

1//! Transportation and Logistics Optimization
2//!
3//! This module provides optimization solutions for the transportation industry,
4//! including traffic flow optimization, vehicle routing problems, logistics planning,
5//! smart city infrastructure optimization, and autonomous vehicle coordination.
6
7use super::{
8    ApplicationError, ApplicationResult, IndustryConstraint, IndustryObjective, IndustrySolution,
9    OptimizationProblem,
10};
11use crate::ising::IsingModel;
12use crate::qubo::{QuboBuilder, QuboFormulation};
13use crate::simulator::{AnnealingParams, ClassicalAnnealingSimulator};
14use std::collections::HashMap;
15
16use std::fmt::Write;
17/// Vehicle Routing Problem with Time Windows
18#[derive(Debug, Clone)]
19pub struct VehicleRoutingProblem {
20    /// Number of vehicles
21    pub num_vehicles: usize,
22    /// Number of customer locations
23    pub num_customers: usize,
24    /// Distance matrix between locations (includes depot at index 0)
25    pub distance_matrix: Vec<Vec<f64>>,
26    /// Customer demands
27    pub customer_demands: Vec<f64>,
28    /// Vehicle capacities
29    pub vehicle_capacities: Vec<f64>,
30    /// Time windows for each customer [`start_time`, `end_time`]
31    pub time_windows: Vec<(f64, f64)>,
32    /// Service times at each location
33    pub service_times: Vec<f64>,
34    /// Maximum route duration
35    pub max_route_duration: f64,
36    /// Vehicle operating costs per unit distance
37    pub vehicle_costs: Vec<f64>,
38    /// Priority weights for customers
39    pub customer_priorities: Vec<f64>,
40}
41
42impl VehicleRoutingProblem {
43    /// Create a new vehicle routing problem
44    pub fn new(
45        num_vehicles: usize,
46        num_customers: usize,
47        distance_matrix: Vec<Vec<f64>>,
48        customer_demands: Vec<f64>,
49        vehicle_capacities: Vec<f64>,
50    ) -> ApplicationResult<Self> {
51        let total_locations = num_customers + 1; // +1 for depot
52
53        if distance_matrix.len() != total_locations {
54            return Err(ApplicationError::InvalidConfiguration(
55                "Distance matrix dimension mismatch".to_string(),
56            ));
57        }
58
59        if customer_demands.len() != num_customers {
60            return Err(ApplicationError::InvalidConfiguration(
61                "Customer demands length mismatch".to_string(),
62            ));
63        }
64
65        if vehicle_capacities.len() != num_vehicles {
66            return Err(ApplicationError::InvalidConfiguration(
67                "Vehicle capacities length mismatch".to_string(),
68            ));
69        }
70
71        Ok(Self {
72            num_vehicles,
73            num_customers,
74            distance_matrix,
75            customer_demands,
76            vehicle_capacities,
77            time_windows: vec![(0.0, 1000.0); num_customers + 1], // Default wide windows
78            service_times: vec![5.0; num_customers + 1],          // Default 5 minute service
79            max_route_duration: 480.0,                            // 8 hours
80            vehicle_costs: vec![1.0; num_vehicles],               // Default cost per distance unit
81            customer_priorities: vec![1.0; num_customers],        // Equal priority
82        })
83    }
84
85    /// Set time windows for customers
86    pub fn set_time_windows(&mut self, time_windows: Vec<(f64, f64)>) -> ApplicationResult<()> {
87        if time_windows.len() != self.num_customers + 1 {
88            return Err(ApplicationError::InvalidConfiguration(
89                "Time windows length mismatch".to_string(),
90            ));
91        }
92        self.time_windows = time_windows;
93        Ok(())
94    }
95
96    /// Calculate total route distance
97    #[must_use]
98    pub fn calculate_route_distance(&self, route: &[usize]) -> f64 {
99        if route.len() < 2 {
100            return 0.0;
101        }
102
103        let mut total_distance = 0.0;
104        for window in route.windows(2) {
105            total_distance += self.distance_matrix[window[0]][window[1]];
106        }
107        total_distance
108    }
109
110    /// Check if route satisfies capacity constraints
111    #[must_use]
112    pub fn check_capacity_constraint(&self, route: &[usize], vehicle_idx: usize) -> bool {
113        let total_demand: f64 = route.iter()
114            .skip(1) // Skip depot
115            .take(route.len() - 2) // Skip return to depot
116            .map(|&customer| self.customer_demands[customer - 1]) // Adjust for depot offset
117            .sum();
118
119        total_demand <= self.vehicle_capacities[vehicle_idx]
120    }
121
122    /// Check time window feasibility
123    #[must_use]
124    pub fn check_time_windows(&self, route: &[usize]) -> bool {
125        let mut current_time = 0.0;
126
127        for i in 1..route.len() {
128            let prev_location = route[i - 1];
129            let current_location = route[i];
130
131            // Travel time
132            current_time += self.distance_matrix[prev_location][current_location] / 50.0; // Assume 50 km/h
133
134            // Check time window
135            let (earliest, latest) = self.time_windows[current_location];
136            if current_time > latest {
137                return false; // Arrived too late
138            }
139
140            // Wait if arrived too early
141            if current_time < earliest {
142                current_time = earliest;
143            }
144
145            // Add service time
146            current_time += self.service_times[current_location];
147        }
148
149        current_time <= self.max_route_duration
150    }
151}
152
153impl OptimizationProblem for VehicleRoutingProblem {
154    type Solution = VehicleRoutingSolution;
155    type ObjectiveValue = f64;
156
157    fn description(&self) -> String {
158        format!(
159            "Vehicle routing problem with {} vehicles and {} customers",
160            self.num_vehicles, self.num_customers
161        )
162    }
163
164    fn size_metrics(&self) -> HashMap<String, usize> {
165        let mut metrics = HashMap::new();
166        metrics.insert("num_vehicles".to_string(), self.num_vehicles);
167        metrics.insert("num_customers".to_string(), self.num_customers);
168        metrics.insert("total_locations".to_string(), self.num_customers + 1);
169        metrics
170    }
171
172    fn validate(&self) -> ApplicationResult<()> {
173        if self.num_vehicles == 0 {
174            return Err(ApplicationError::DataValidationError(
175                "At least one vehicle required".to_string(),
176            ));
177        }
178
179        if self.num_customers == 0 {
180            return Err(ApplicationError::DataValidationError(
181                "At least one customer required".to_string(),
182            ));
183        }
184
185        // Check positive demands
186        for &demand in &self.customer_demands {
187            if demand < 0.0 {
188                return Err(ApplicationError::DataValidationError(
189                    "Customer demands must be non-negative".to_string(),
190                ));
191            }
192        }
193
194        // Check positive capacities
195        for &capacity in &self.vehicle_capacities {
196            if capacity <= 0.0 {
197                return Err(ApplicationError::DataValidationError(
198                    "Vehicle capacities must be positive".to_string(),
199                ));
200            }
201        }
202
203        Ok(())
204    }
205
206    fn to_qubo(&self) -> ApplicationResult<(crate::ising::QuboModel, HashMap<String, usize>)> {
207        let mut builder = QuboBuilder::new();
208
209        // Binary variables: x[i][j][k] = 1 if vehicle k travels from location i to j
210        let total_locations = self.num_customers + 1;
211        let num_vars = total_locations * total_locations * self.num_vehicles;
212        let mut string_var_map = HashMap::new();
213
214        // Helper function to get variable index
215        let var_index = |i: usize, j: usize, k: usize| -> usize {
216            i * total_locations * self.num_vehicles + j * self.num_vehicles + k
217        };
218
219        // Create string variable mapping
220        for i in 0..total_locations {
221            for j in 0..total_locations {
222                if i != j {
223                    for k in 0..self.num_vehicles {
224                        let var_idx = var_index(i, j, k);
225                        let var_name = format!("x_{i}_{j}_{k}");
226                        string_var_map.insert(var_name, var_idx);
227                    }
228                }
229            }
230        }
231
232        // Objective: minimize total distance and costs
233        for i in 0..total_locations {
234            for j in 0..total_locations {
235                if i != j {
236                    for k in 0..self.num_vehicles {
237                        let var_idx = var_index(i, j, k);
238                        let cost = self.distance_matrix[i][j] * self.vehicle_costs[k];
239                        builder.add_bias(var_idx, cost);
240                    }
241                }
242            }
243        }
244
245        // Constraint: each customer visited exactly once
246        let visit_penalty = 1000.0;
247        for customer in 1..total_locations {
248            for i in 0..total_locations {
249                if i != customer {
250                    for k1 in 0..self.num_vehicles {
251                        let var1 = var_index(i, customer, k1);
252                        builder.add_bias(var1, -visit_penalty);
253
254                        for j in 0..total_locations {
255                            if j != customer {
256                                for k2 in 0..self.num_vehicles {
257                                    if (i, k1) != (j, k2) {
258                                        let var2 = var_index(j, customer, k2);
259                                        builder.add_coupling(var1, var2, visit_penalty);
260                                    }
261                                }
262                            }
263                        }
264                    }
265                }
266            }
267        }
268
269        // Constraint: flow conservation (what comes in must go out)
270        let flow_penalty = 800.0;
271        for location in 0..total_locations {
272            for k in 0..self.num_vehicles {
273                let mut in_vars = Vec::new();
274                let mut out_vars = Vec::new();
275
276                for i in 0..total_locations {
277                    if i != location {
278                        in_vars.push(var_index(i, location, k));
279                    }
280                }
281
282                for j in 0..total_locations {
283                    if j != location {
284                        out_vars.push(var_index(location, j, k));
285                    }
286                }
287
288                // Add flow conservation penalty
289                for &in_var in &in_vars {
290                    for &out_var in &out_vars {
291                        builder.add_coupling(in_var, out_var, -flow_penalty);
292                    }
293                }
294            }
295        }
296
297        // Capacity constraints (simplified penalty)
298        let capacity_penalty = 500.0;
299        for k in 0..self.num_vehicles {
300            for customer in 1..total_locations {
301                let demand = self.customer_demands[customer - 1];
302                let capacity_violation = (demand / self.vehicle_capacities[k]).max(0.0);
303
304                for i in 0..total_locations {
305                    if i != customer {
306                        let var_idx = var_index(i, customer, k);
307                        builder.add_bias(var_idx, capacity_penalty * capacity_violation);
308                    }
309                }
310            }
311        }
312
313        Ok((builder.build(), string_var_map))
314    }
315
316    fn evaluate_solution(
317        &self,
318        solution: &Self::Solution,
319    ) -> ApplicationResult<Self::ObjectiveValue> {
320        let mut total_cost = 0.0;
321
322        for (vehicle_idx, route) in solution.routes.iter().enumerate() {
323            if !route.is_empty() {
324                let distance = self.calculate_route_distance(route);
325                total_cost += distance * self.vehicle_costs[vehicle_idx];
326
327                // Add penalty for constraint violations
328                if !self.check_capacity_constraint(route, vehicle_idx) {
329                    total_cost += 10_000.0; // Large penalty
330                }
331
332                if !self.check_time_windows(route) {
333                    total_cost += 5000.0; // Time window penalty
334                }
335            }
336        }
337
338        Ok(total_cost)
339    }
340
341    fn is_feasible(&self, solution: &Self::Solution) -> bool {
342        // Check that all customers are visited exactly once
343        let mut visited_customers = vec![false; self.num_customers];
344
345        for route in &solution.routes {
346            for &location in route {
347                if location > 0 && location <= self.num_customers {
348                    if visited_customers[location - 1] {
349                        return false; // Customer visited more than once
350                    }
351                    visited_customers[location - 1] = true;
352                }
353            }
354        }
355
356        // Check all customers are visited
357        if !visited_customers.iter().all(|&visited| visited) {
358            return false;
359        }
360
361        // Check capacity and time window constraints for each route
362        for (vehicle_idx, route) in solution.routes.iter().enumerate() {
363            if !route.is_empty() {
364                if !self.check_capacity_constraint(route, vehicle_idx) {
365                    return false;
366                }
367
368                if !self.check_time_windows(route) {
369                    return false;
370                }
371            }
372        }
373
374        true
375    }
376}
377
378/// Vehicle routing solution
379#[derive(Debug, Clone)]
380pub struct VehicleRoutingSolution {
381    /// Routes for each vehicle (sequence of location indices)
382    pub routes: Vec<Vec<usize>>,
383    /// Total distance traveled
384    pub total_distance: f64,
385    /// Total cost
386    pub total_cost: f64,
387    /// Route statistics
388    pub route_stats: Vec<RouteStatistics>,
389    /// Transportation metrics
390    pub metrics: TransportationMetrics,
391}
392
393/// Statistics for individual routes
394#[derive(Debug, Clone)]
395pub struct RouteStatistics {
396    /// Route distance
397    pub distance: f64,
398    /// Route duration
399    pub duration: f64,
400    /// Total demand served
401    pub total_demand: f64,
402    /// Number of customers served
403    pub num_customers: usize,
404    /// Capacity utilization
405    pub capacity_utilization: f64,
406    /// Time window compliance
407    pub time_window_compliance: bool,
408}
409
410/// Transportation performance metrics
411#[derive(Debug, Clone)]
412pub struct TransportationMetrics {
413    /// Fleet utilization (fraction of vehicles used)
414    pub fleet_utilization: f64,
415    /// Average route distance
416    pub avg_route_distance: f64,
417    /// Average route duration
418    pub avg_route_duration: f64,
419    /// Total fuel consumption estimate
420    pub fuel_consumption: f64,
421    /// CO2 emissions estimate (kg)
422    pub co2_emissions: f64,
423    /// Service level (customers served on time)
424    pub service_level: f64,
425    /// Cost per customer served
426    pub cost_per_customer: f64,
427}
428
429impl IndustrySolution for VehicleRoutingSolution {
430    type Problem = VehicleRoutingProblem;
431
432    fn from_binary(problem: &Self::Problem, binary_solution: &[i8]) -> ApplicationResult<Self> {
433        let total_locations = problem.num_customers + 1;
434        let num_vars = total_locations * total_locations * problem.num_vehicles;
435
436        // Decode binary solution to routes
437        let mut routes = vec![Vec::new(); problem.num_vehicles];
438
439        // Helper function to get variable index
440        let var_index = |i: usize, j: usize, k: usize| -> usize {
441            i * total_locations * problem.num_vehicles + j * total_locations + k
442        };
443
444        // Build routes from binary variables
445        for k in 0..problem.num_vehicles {
446            let mut current_location = 0; // Start at depot
447            let mut route = vec![0]; // Include depot
448            let mut visited = vec![false; total_locations];
449            visited[0] = true;
450
451            // Follow the path indicated by the binary solution
452            while route.len() < total_locations && route.len() < 10 {
453                // Prevent infinite loops
454                let mut next_location = None;
455
456                for j in 0..total_locations {
457                    if !visited[j] {
458                        let var_idx = var_index(current_location, j, k);
459                        if var_idx < binary_solution.len() && binary_solution[var_idx] > 0 {
460                            next_location = Some(j);
461                            break;
462                        }
463                    }
464                }
465
466                if let Some(next) = next_location {
467                    route.push(next);
468                    visited[next] = true;
469                    current_location = next;
470                } else {
471                    break;
472                }
473            }
474
475            // Return to depot if we visited customers
476            if route.len() > 1 {
477                route.push(0);
478            }
479
480            routes[k] = route;
481        }
482
483        // Calculate statistics
484        let mut total_distance = 0.0;
485        let mut route_stats = Vec::new();
486        let mut vehicles_used = 0;
487
488        for (vehicle_idx, route) in routes.iter().enumerate() {
489            if route.len() > 2 {
490                // More than just depot->depot
491                vehicles_used += 1;
492                let distance = problem.calculate_route_distance(route);
493                total_distance += distance;
494
495                let total_demand: f64 = route
496                    .iter()
497                    .skip(1)
498                    .take(route.len() - 2)
499                    .map(|&customer| problem.customer_demands[customer - 1])
500                    .sum();
501
502                let capacity_utilization = total_demand / problem.vehicle_capacities[vehicle_idx];
503                let duration = (route.len() as f64).mul_add(5.0, distance / 50.0); // Travel + service time
504
505                route_stats.push(RouteStatistics {
506                    distance,
507                    duration,
508                    total_demand,
509                    num_customers: route.len().saturating_sub(2),
510                    capacity_utilization,
511                    time_window_compliance: problem.check_time_windows(route),
512                });
513            } else {
514                route_stats.push(RouteStatistics {
515                    distance: 0.0,
516                    duration: 0.0,
517                    total_demand: 0.0,
518                    num_customers: 0,
519                    capacity_utilization: 0.0,
520                    time_window_compliance: true,
521                });
522            }
523        }
524
525        let total_cost =
526            ((problem.num_vehicles - vehicles_used) as f64).mul_add(100.0, total_distance); // Penalty for unused vehicles
527
528        // Calculate transportation metrics
529        let fleet_utilization = vehicles_used as f64 / problem.num_vehicles as f64;
530        let avg_route_distance = if vehicles_used > 0 {
531            total_distance / vehicles_used as f64
532        } else {
533            0.0
534        };
535        let avg_route_duration = route_stats
536            .iter()
537            .filter(|s| s.num_customers > 0)
538            .map(|s| s.duration)
539            .sum::<f64>()
540            / vehicles_used.max(1) as f64;
541
542        let fuel_consumption = total_distance * 0.1; // 0.1 L/km estimate
543        let co2_emissions = fuel_consumption * 2.3; // 2.3 kg CO2/L estimate
544
545        let customers_on_time = route_stats
546            .iter()
547            .filter(|s| s.time_window_compliance)
548            .map(|s| s.num_customers)
549            .sum::<usize>();
550        let service_level = customers_on_time as f64 / problem.num_customers as f64;
551
552        let cost_per_customer = if problem.num_customers > 0 {
553            total_cost / problem.num_customers as f64
554        } else {
555            0.0
556        };
557
558        let metrics = TransportationMetrics {
559            fleet_utilization,
560            avg_route_distance,
561            avg_route_duration,
562            fuel_consumption,
563            co2_emissions,
564            service_level,
565            cost_per_customer,
566        };
567
568        Ok(Self {
569            routes,
570            total_distance,
571            total_cost,
572            route_stats,
573            metrics,
574        })
575    }
576
577    fn summary(&self) -> HashMap<String, String> {
578        let mut summary = HashMap::new();
579        summary.insert("type".to_string(), "Vehicle Routing".to_string());
580        summary.insert(
581            "total_distance".to_string(),
582            format!("{:.1} km", self.total_distance),
583        );
584        summary.insert("total_cost".to_string(), format!("${:.2}", self.total_cost));
585        summary.insert(
586            "fleet_utilization".to_string(),
587            format!("{:.1}%", self.metrics.fleet_utilization * 100.0),
588        );
589        summary.insert(
590            "service_level".to_string(),
591            format!("{:.1}%", self.metrics.service_level * 100.0),
592        );
593        summary.insert(
594            "fuel_consumption".to_string(),
595            format!("{:.1} L", self.metrics.fuel_consumption),
596        );
597        summary.insert(
598            "co2_emissions".to_string(),
599            format!("{:.1} kg", self.metrics.co2_emissions),
600        );
601
602        let vehicles_used = self.routes.iter().filter(|r| r.len() > 2).count();
603        summary.insert("vehicles_used".to_string(), vehicles_used.to_string());
604
605        summary
606    }
607
608    fn metrics(&self) -> HashMap<String, f64> {
609        let mut metrics = HashMap::new();
610        metrics.insert("total_distance".to_string(), self.total_distance);
611        metrics.insert("total_cost".to_string(), self.total_cost);
612        metrics.insert(
613            "fleet_utilization".to_string(),
614            self.metrics.fleet_utilization,
615        );
616        metrics.insert(
617            "avg_route_distance".to_string(),
618            self.metrics.avg_route_distance,
619        );
620        metrics.insert(
621            "avg_route_duration".to_string(),
622            self.metrics.avg_route_duration,
623        );
624        metrics.insert(
625            "fuel_consumption".to_string(),
626            self.metrics.fuel_consumption,
627        );
628        metrics.insert("co2_emissions".to_string(), self.metrics.co2_emissions);
629        metrics.insert("service_level".to_string(), self.metrics.service_level);
630        metrics.insert(
631            "cost_per_customer".to_string(),
632            self.metrics.cost_per_customer,
633        );
634
635        let vehicles_used = self.routes.iter().filter(|r| r.len() > 2).count();
636        metrics.insert("vehicles_used".to_string(), vehicles_used as f64);
637
638        metrics
639    }
640
641    fn export_format(&self) -> ApplicationResult<String> {
642        let mut output = String::new();
643        output.push_str("# Vehicle Routing Optimization Report\n\n");
644
645        output.push_str("## Summary\n");
646        writeln!(output, "Total Distance: {:.1} km", self.total_distance)
647            .expect("Writing to String should not fail");
648        writeln!(output, "Total Cost: ${:.2}", self.total_cost)
649            .expect("Writing to String should not fail");
650        write!(
651            output,
652            "Fleet Utilization: {:.1}%\n",
653            self.metrics.fleet_utilization * 100.0
654        )
655        .expect("Writing to String should not fail");
656        write!(
657            output,
658            "Service Level: {:.1}%\n",
659            self.metrics.service_level * 100.0
660        )
661        .expect("Writing to String should not fail");
662
663        output.push_str("\n## Environmental Impact\n");
664        write!(
665            output,
666            "Fuel Consumption: {:.1} L\n",
667            self.metrics.fuel_consumption
668        )
669        .expect("Writing to String should not fail");
670        write!(
671            output,
672            "CO2 Emissions: {:.1} kg\n",
673            self.metrics.co2_emissions
674        )
675        .expect("Writing to String should not fail");
676
677        output.push_str("\n## Route Details\n");
678        for (i, (route, stats)) in self.routes.iter().zip(&self.route_stats).enumerate() {
679            if route.len() > 2 {
680                write!(output, "Vehicle {}: ", i + 1).expect("Writing to String should not fail");
681                write!(output, "Route {route:?}, ").expect("Writing to String should not fail");
682                write!(output, "Distance: {:.1} km, ", stats.distance)
683                    .expect("Writing to String should not fail");
684                write!(output, "Duration: {:.1} h, ", stats.duration / 60.0)
685                    .expect("Writing to String should not fail");
686                write!(output, "Customers: {}, ", stats.num_customers)
687                    .expect("Writing to String should not fail");
688                write!(
689                    output,
690                    "Capacity: {:.1}%\n",
691                    stats.capacity_utilization * 100.0
692                )
693                .expect("Writing to String should not fail");
694            }
695        }
696
697        Ok(output)
698    }
699}
700
701/// Traffic Flow Optimization Problem
702#[derive(Debug, Clone)]
703pub struct TrafficFlowOptimization {
704    /// Number of intersections
705    pub num_intersections: usize,
706    /// Number of road segments
707    pub num_segments: usize,
708    /// Traffic flow rates between intersections
709    pub flow_matrix: Vec<Vec<f64>>,
710    /// Road segment capacities
711    pub segment_capacities: Vec<f64>,
712    /// Traffic light cycle times (seconds)
713    pub cycle_times: Vec<f64>,
714    /// Green time allocations for each direction
715    pub green_time_allocations: Vec<Vec<f64>>,
716    /// Priority routes (emergency, public transport)
717    pub priority_routes: Vec<PriorityRoute>,
718    /// Environmental constraints
719    pub emission_limits: Vec<f64>,
720}
721
722/// Priority route definition
723#[derive(Debug, Clone)]
724pub struct PriorityRoute {
725    /// Route path (sequence of intersections)
726    pub path: Vec<usize>,
727    /// Priority level (1-10, higher is more important)
728    pub priority: usize,
729    /// Maximum allowed delay (seconds)
730    pub max_delay: f64,
731    /// Route type
732    pub route_type: RouteType,
733}
734
735/// Types of priority routes
736#[derive(Debug, Clone, PartialEq, Eq)]
737pub enum RouteType {
738    /// Emergency services
739    Emergency,
740    /// Public transportation
741    PublicTransport,
742    /// Commercial delivery
743    Commercial,
744    /// Regular traffic
745    Regular,
746}
747
748impl TrafficFlowOptimization {
749    /// Create a new traffic flow optimization problem
750    pub fn new(
751        num_intersections: usize,
752        num_segments: usize,
753        flow_matrix: Vec<Vec<f64>>,
754        segment_capacities: Vec<f64>,
755    ) -> ApplicationResult<Self> {
756        if flow_matrix.len() != num_intersections {
757            return Err(ApplicationError::InvalidConfiguration(
758                "Flow matrix dimension mismatch".to_string(),
759            ));
760        }
761
762        if segment_capacities.len() != num_segments {
763            return Err(ApplicationError::InvalidConfiguration(
764                "Segment capacities length mismatch".to_string(),
765            ));
766        }
767
768        Ok(Self {
769            num_intersections,
770            num_segments,
771            flow_matrix,
772            segment_capacities,
773            cycle_times: vec![90.0; num_intersections], // Default 90 second cycles
774            green_time_allocations: vec![vec![30.0, 30.0, 15.0, 15.0]; num_intersections], // 4-direction default
775            priority_routes: Vec::new(),
776            emission_limits: vec![100.0; num_intersections], // kg CO2/hour
777        })
778    }
779
780    /// Add priority route
781    pub fn add_priority_route(&mut self, route: PriorityRoute) {
782        self.priority_routes.push(route);
783    }
784
785    /// Calculate total travel time for given signal timing
786    #[must_use]
787    pub fn calculate_total_travel_time(&self, solution: &TrafficFlowSolution) -> f64 {
788        let mut total_time = 0.0;
789
790        for i in 0..self.num_intersections {
791            for j in 0..self.num_intersections {
792                if i != j && self.flow_matrix[i][j] > 0.0 {
793                    let flow = self.flow_matrix[i][j];
794                    let delay = self.calculate_intersection_delay(i, &solution.signal_timings[i]);
795                    total_time += flow * delay;
796                }
797            }
798        }
799
800        total_time
801    }
802
803    /// Calculate delay at intersection based on signal timing
804    fn calculate_intersection_delay(
805        &self,
806        intersection: usize,
807        timing: &IntersectionTiming,
808    ) -> f64 {
809        // Simplified delay calculation using Webster's formula
810        let cycle_time = timing.cycle_time;
811        let effective_green = timing.green_times.iter().sum::<f64>();
812        let flow_ratio = 0.8; // Simplified
813
814        if effective_green <= 0.0 {
815            return 1000.0; // Very high delay for invalid timing
816        }
817
818        let delay = (cycle_time * (1.0 - effective_green / cycle_time).powi(2))
819            / (2.0 * (1.0 - flow_ratio * effective_green / cycle_time));
820
821        delay.clamp(0.0, 1000.0) // Cap at reasonable values
822    }
823}
824
825/// Traffic flow solution
826#[derive(Debug, Clone)]
827pub struct TrafficFlowSolution {
828    /// Signal timing for each intersection
829    pub signal_timings: Vec<IntersectionTiming>,
830    /// Total travel time
831    pub total_travel_time: f64,
832    /// Average delay per vehicle
833    pub average_delay: f64,
834    /// Throughput metrics
835    pub throughput_metrics: ThroughputMetrics,
836    /// Environmental impact
837    pub environmental_impact: EnvironmentalImpact,
838}
839
840/// Signal timing for an intersection
841#[derive(Debug, Clone)]
842pub struct IntersectionTiming {
843    /// Total cycle time (seconds)
844    pub cycle_time: f64,
845    /// Green times for each direction (seconds)
846    pub green_times: Vec<f64>,
847    /// Offset from master clock (seconds)
848    pub offset: f64,
849    /// Coordination with adjacent intersections
850    pub coordination_factor: f64,
851}
852
853/// Traffic throughput metrics
854#[derive(Debug, Clone)]
855pub struct ThroughputMetrics {
856    /// Total vehicles processed per hour
857    pub vehicles_per_hour: f64,
858    /// Average speed (km/h)
859    pub average_speed: f64,
860    /// Capacity utilization
861    pub capacity_utilization: f64,
862    /// Queue lengths
863    pub average_queue_length: f64,
864    /// Level of service grade
865    pub level_of_service: String,
866}
867
868/// Environmental impact metrics
869#[derive(Debug, Clone)]
870pub struct EnvironmentalImpact {
871    /// CO2 emissions (kg/hour)
872    pub co2_emissions: f64,
873    /// `NOx` emissions (kg/hour)
874    pub nox_emissions: f64,
875    /// Fuel consumption (L/hour)
876    pub fuel_consumption: f64,
877    /// Noise level (dB)
878    pub noise_level: f64,
879}
880
881/// Binary wrapper for Vehicle Routing Problem that works with binary solutions
882#[derive(Debug, Clone)]
883pub struct BinaryVehicleRoutingProblem {
884    inner: VehicleRoutingProblem,
885}
886
887impl BinaryVehicleRoutingProblem {
888    #[must_use]
889    pub const fn new(inner: VehicleRoutingProblem) -> Self {
890        Self { inner }
891    }
892}
893
894impl OptimizationProblem for BinaryVehicleRoutingProblem {
895    type Solution = Vec<i8>;
896    type ObjectiveValue = f64;
897
898    fn description(&self) -> String {
899        self.inner.description()
900    }
901
902    fn size_metrics(&self) -> HashMap<String, usize> {
903        self.inner.size_metrics()
904    }
905
906    fn validate(&self) -> ApplicationResult<()> {
907        self.inner.validate()
908    }
909
910    fn to_qubo(&self) -> ApplicationResult<(crate::ising::QuboModel, HashMap<String, usize>)> {
911        self.inner.to_qubo()
912    }
913
914    fn evaluate_solution(
915        &self,
916        solution: &Self::Solution,
917    ) -> ApplicationResult<Self::ObjectiveValue> {
918        // Convert binary solution to VehicleRoutingSolution for evaluation
919        let routing_solution = VehicleRoutingSolution::from_binary(&self.inner, solution)?;
920        self.inner.evaluate_solution(&routing_solution)
921    }
922
923    fn is_feasible(&self, solution: &Self::Solution) -> bool {
924        // Convert binary solution to VehicleRoutingSolution for feasibility check
925        if let Ok(routing_solution) = VehicleRoutingSolution::from_binary(&self.inner, solution) {
926            self.inner.is_feasible(&routing_solution)
927        } else {
928            false
929        }
930    }
931}
932
933/// Create benchmark transportation problems
934pub fn create_benchmark_problems(
935    size: usize,
936) -> ApplicationResult<Vec<Box<dyn OptimizationProblem<Solution = Vec<i8>, ObjectiveValue = f64>>>>
937{
938    let mut problems = Vec::new();
939
940    // Problem 1: Small vehicle routing problem
941    let distance_matrix = vec![
942        vec![0.0, 10.0, 15.0, 20.0], // Depot distances
943        vec![10.0, 0.0, 8.0, 12.0],  // Customer 1
944        vec![15.0, 8.0, 0.0, 9.0],   // Customer 2
945        vec![20.0, 12.0, 9.0, 0.0],  // Customer 3
946    ];
947
948    let customer_demands = vec![5.0, 8.0, 3.0];
949    let vehicle_capacities = vec![15.0, 12.0];
950
951    let vrp = VehicleRoutingProblem::new(
952        2, // 2 vehicles
953        3, // 3 customers
954        distance_matrix,
955        customer_demands,
956        vehicle_capacities,
957    )?;
958
959    problems.push(Box::new(BinaryVehicleRoutingProblem::new(vrp))
960        as Box<
961            dyn OptimizationProblem<Solution = Vec<i8>, ObjectiveValue = f64>,
962        >);
963
964    // Problem 2: Larger VRP for complex scenarios
965    if size >= 6 {
966        let mut large_distance_matrix = vec![vec![0.0; size + 1]; size + 1];
967
968        // Generate random but realistic distances
969        for i in 0..=size {
970            for j in 0..=size {
971                if i != j {
972                    large_distance_matrix[i][j] = (i as f64 - j as f64).abs().mul_add(5.0, 10.0);
973                }
974            }
975        }
976
977        let large_demands = vec![5.0; size];
978        let large_capacities = vec![20.0; size / 2];
979
980        let large_vrp = VehicleRoutingProblem::new(
981            size / 2,
982            size,
983            large_distance_matrix,
984            large_demands,
985            large_capacities,
986        )?;
987
988        problems.push(Box::new(BinaryVehicleRoutingProblem::new(large_vrp))
989            as Box<
990                dyn OptimizationProblem<Solution = Vec<i8>, ObjectiveValue = f64>,
991            >);
992    }
993
994    Ok(problems)
995}
996
997/// Solve vehicle routing problem using quantum annealing
998pub fn solve_vehicle_routing(
999    problem: &VehicleRoutingProblem,
1000    params: Option<AnnealingParams>,
1001) -> ApplicationResult<VehicleRoutingSolution> {
1002    // Convert to QUBO
1003    let (qubo, _var_map) = problem.to_qubo()?;
1004
1005    // Convert to Ising
1006    let ising = IsingModel::from_qubo(&qubo);
1007
1008    // Set up annealing parameters
1009    let annealing_params = params.unwrap_or_else(|| {
1010        let mut p = AnnealingParams::default();
1011        p.num_sweeps = 30_000;
1012        p.num_repetitions = 50;
1013        p.initial_temperature = 8.0;
1014        p.final_temperature = 0.001;
1015        p
1016    });
1017
1018    // Solve with classical annealing
1019    let simulator = ClassicalAnnealingSimulator::new(annealing_params)
1020        .map_err(|e| ApplicationError::OptimizationError(e.to_string()))?;
1021
1022    let result = simulator
1023        .solve(&ising)
1024        .map_err(|e| ApplicationError::OptimizationError(e.to_string()))?;
1025
1026    // Convert solution back to vehicle routing solution
1027    VehicleRoutingSolution::from_binary(problem, &result.best_spins)
1028}
1029
1030#[cfg(test)]
1031mod tests {
1032    use super::*;
1033
1034    #[test]
1035    fn test_vehicle_routing_creation() {
1036        let distance_matrix = vec![
1037            vec![0.0, 10.0, 15.0],
1038            vec![10.0, 0.0, 8.0],
1039            vec![15.0, 8.0, 0.0],
1040        ];
1041        let demands = vec![5.0, 8.0];
1042        let capacities = vec![15.0];
1043
1044        let vrp = VehicleRoutingProblem::new(1, 2, distance_matrix, demands, capacities)
1045            .expect("VehicleRoutingProblem creation should succeed");
1046        assert_eq!(vrp.num_vehicles, 1);
1047        assert_eq!(vrp.num_customers, 2);
1048    }
1049
1050    #[test]
1051    fn test_route_distance_calculation() {
1052        let distance_matrix = vec![
1053            vec![0.0, 10.0, 15.0],
1054            vec![10.0, 0.0, 8.0],
1055            vec![15.0, 8.0, 0.0],
1056        ];
1057        let demands = vec![5.0, 8.0];
1058        let capacities = vec![15.0];
1059
1060        let vrp = VehicleRoutingProblem::new(1, 2, distance_matrix, demands, capacities)
1061            .expect("VehicleRoutingProblem creation should succeed");
1062
1063        let route = vec![0, 1, 2, 0]; // Depot -> Customer 1 -> Customer 2 -> Depot
1064        let distance = vrp.calculate_route_distance(&route);
1065        assert_eq!(distance, 10.0 + 8.0 + 15.0); // 33.0
1066    }
1067
1068    #[test]
1069    fn test_capacity_constraint() {
1070        let distance_matrix = vec![vec![0.0; 3]; 3];
1071        let demands = vec![5.0, 8.0];
1072        let capacities = vec![10.0]; // Small capacity
1073
1074        let vrp = VehicleRoutingProblem::new(1, 2, distance_matrix, demands, capacities)
1075            .expect("VehicleRoutingProblem creation should succeed");
1076
1077        let route = vec![0, 1, 2, 0]; // Both customers
1078        assert!(!vrp.check_capacity_constraint(&route, 0)); // Should exceed capacity
1079
1080        let route2 = vec![0, 1, 0]; // Only customer 1
1081        assert!(vrp.check_capacity_constraint(&route2, 0)); // Should be within capacity
1082    }
1083
1084    #[test]
1085    fn test_traffic_flow_creation() {
1086        let flow_matrix = vec![
1087            vec![0.0, 100.0, 50.0],
1088            vec![80.0, 0.0, 120.0],
1089            vec![60.0, 90.0, 0.0],
1090        ];
1091        let capacities = vec![200.0, 180.0, 150.0];
1092
1093        let traffic = TrafficFlowOptimization::new(3, 3, flow_matrix, capacities)
1094            .expect("TrafficFlowOptimization creation should succeed");
1095        assert_eq!(traffic.num_intersections, 3);
1096        assert_eq!(traffic.num_segments, 3);
1097    }
1098
1099    #[test]
1100    fn test_vrp_validation() {
1101        let distance_matrix = vec![vec![0.0; 3]; 3];
1102        let demands = vec![5.0, 8.0];
1103        let capacities = vec![15.0];
1104
1105        let vrp = VehicleRoutingProblem::new(
1106            1,
1107            2,
1108            distance_matrix.clone(),
1109            demands.clone(),
1110            capacities.clone(),
1111        )
1112        .expect("VehicleRoutingProblem creation should succeed");
1113        assert!(vrp.validate().is_ok());
1114
1115        // Test invalid VRP (zero vehicles)
1116        let invalid_vrp = VehicleRoutingProblem::new(0, 2, distance_matrix, demands, capacities);
1117        // Should create successfully but fail validation
1118        // Note: Constructor might return error before we get to validate
1119    }
1120
1121    #[test]
1122    fn test_benchmark_problems() {
1123        let problems =
1124            create_benchmark_problems(4).expect("Creating benchmark problems should succeed");
1125        assert_eq!(problems.len(), 1); // Size 4 gets only the small problem
1126
1127        let larger_problems = create_benchmark_problems(6)
1128            .expect("Creating larger benchmark problems should succeed");
1129        assert_eq!(larger_problems.len(), 2); // Size 6 gets both problems
1130
1131        for problem in &larger_problems {
1132            assert!(problem.validate().is_ok());
1133        }
1134    }
1135}