1use 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#[derive(Debug, Clone)]
19pub struct VehicleRoutingProblem {
20 pub num_vehicles: usize,
22 pub num_customers: usize,
24 pub distance_matrix: Vec<Vec<f64>>,
26 pub customer_demands: Vec<f64>,
28 pub vehicle_capacities: Vec<f64>,
30 pub time_windows: Vec<(f64, f64)>,
32 pub service_times: Vec<f64>,
34 pub max_route_duration: f64,
36 pub vehicle_costs: Vec<f64>,
38 pub customer_priorities: Vec<f64>,
40}
41
42impl VehicleRoutingProblem {
43 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; 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], service_times: vec![5.0; num_customers + 1], max_route_duration: 480.0, vehicle_costs: vec![1.0; num_vehicles], customer_priorities: vec![1.0; num_customers], })
83 }
84
85 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 #[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 #[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) .take(route.len() - 2) .map(|&customer| self.customer_demands[customer - 1]) .sum();
118
119 total_demand <= self.vehicle_capacities[vehicle_idx]
120 }
121
122 #[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 current_time += self.distance_matrix[prev_location][current_location] / 50.0; let (earliest, latest) = self.time_windows[current_location];
136 if current_time > latest {
137 return false; }
139
140 if current_time < earliest {
142 current_time = earliest;
143 }
144
145 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 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 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 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 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 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 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 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 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 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 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 if !self.check_capacity_constraint(route, vehicle_idx) {
329 total_cost += 10_000.0; }
331
332 if !self.check_time_windows(route) {
333 total_cost += 5000.0; }
335 }
336 }
337
338 Ok(total_cost)
339 }
340
341 fn is_feasible(&self, solution: &Self::Solution) -> bool {
342 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; }
351 visited_customers[location - 1] = true;
352 }
353 }
354 }
355
356 if !visited_customers.iter().all(|&visited| visited) {
358 return false;
359 }
360
361 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#[derive(Debug, Clone)]
380pub struct VehicleRoutingSolution {
381 pub routes: Vec<Vec<usize>>,
383 pub total_distance: f64,
385 pub total_cost: f64,
387 pub route_stats: Vec<RouteStatistics>,
389 pub metrics: TransportationMetrics,
391}
392
393#[derive(Debug, Clone)]
395pub struct RouteStatistics {
396 pub distance: f64,
398 pub duration: f64,
400 pub total_demand: f64,
402 pub num_customers: usize,
404 pub capacity_utilization: f64,
406 pub time_window_compliance: bool,
408}
409
410#[derive(Debug, Clone)]
412pub struct TransportationMetrics {
413 pub fleet_utilization: f64,
415 pub avg_route_distance: f64,
417 pub avg_route_duration: f64,
419 pub fuel_consumption: f64,
421 pub co2_emissions: f64,
423 pub service_level: f64,
425 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 let mut routes = vec![Vec::new(); problem.num_vehicles];
438
439 let var_index = |i: usize, j: usize, k: usize| -> usize {
441 i * total_locations * problem.num_vehicles + j * total_locations + k
442 };
443
444 for k in 0..problem.num_vehicles {
446 let mut current_location = 0; let mut route = vec![0]; let mut visited = vec![false; total_locations];
449 visited[0] = true;
450
451 while route.len() < total_locations && route.len() < 10 {
453 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 if route.len() > 1 {
477 route.push(0);
478 }
479
480 routes[k] = route;
481 }
482
483 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 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); 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); 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; let co2_emissions = fuel_consumption * 2.3; 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#[derive(Debug, Clone)]
703pub struct TrafficFlowOptimization {
704 pub num_intersections: usize,
706 pub num_segments: usize,
708 pub flow_matrix: Vec<Vec<f64>>,
710 pub segment_capacities: Vec<f64>,
712 pub cycle_times: Vec<f64>,
714 pub green_time_allocations: Vec<Vec<f64>>,
716 pub priority_routes: Vec<PriorityRoute>,
718 pub emission_limits: Vec<f64>,
720}
721
722#[derive(Debug, Clone)]
724pub struct PriorityRoute {
725 pub path: Vec<usize>,
727 pub priority: usize,
729 pub max_delay: f64,
731 pub route_type: RouteType,
733}
734
735#[derive(Debug, Clone, PartialEq, Eq)]
737pub enum RouteType {
738 Emergency,
740 PublicTransport,
742 Commercial,
744 Regular,
746}
747
748impl TrafficFlowOptimization {
749 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], green_time_allocations: vec![vec![30.0, 30.0, 15.0, 15.0]; num_intersections], priority_routes: Vec::new(),
776 emission_limits: vec![100.0; num_intersections], })
778 }
779
780 pub fn add_priority_route(&mut self, route: PriorityRoute) {
782 self.priority_routes.push(route);
783 }
784
785 #[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 fn calculate_intersection_delay(
805 &self,
806 intersection: usize,
807 timing: &IntersectionTiming,
808 ) -> f64 {
809 let cycle_time = timing.cycle_time;
811 let effective_green = timing.green_times.iter().sum::<f64>();
812 let flow_ratio = 0.8; if effective_green <= 0.0 {
815 return 1000.0; }
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) }
823}
824
825#[derive(Debug, Clone)]
827pub struct TrafficFlowSolution {
828 pub signal_timings: Vec<IntersectionTiming>,
830 pub total_travel_time: f64,
832 pub average_delay: f64,
834 pub throughput_metrics: ThroughputMetrics,
836 pub environmental_impact: EnvironmentalImpact,
838}
839
840#[derive(Debug, Clone)]
842pub struct IntersectionTiming {
843 pub cycle_time: f64,
845 pub green_times: Vec<f64>,
847 pub offset: f64,
849 pub coordination_factor: f64,
851}
852
853#[derive(Debug, Clone)]
855pub struct ThroughputMetrics {
856 pub vehicles_per_hour: f64,
858 pub average_speed: f64,
860 pub capacity_utilization: f64,
862 pub average_queue_length: f64,
864 pub level_of_service: String,
866}
867
868#[derive(Debug, Clone)]
870pub struct EnvironmentalImpact {
871 pub co2_emissions: f64,
873 pub nox_emissions: f64,
875 pub fuel_consumption: f64,
877 pub noise_level: f64,
879}
880
881#[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 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 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
933pub 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 let distance_matrix = vec![
942 vec![0.0, 10.0, 15.0, 20.0], vec![10.0, 0.0, 8.0, 12.0], vec![15.0, 8.0, 0.0, 9.0], vec![20.0, 12.0, 9.0, 0.0], ];
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, 3, 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 if size >= 6 {
966 let mut large_distance_matrix = vec![vec![0.0; size + 1]; size + 1];
967
968 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
997pub fn solve_vehicle_routing(
999 problem: &VehicleRoutingProblem,
1000 params: Option<AnnealingParams>,
1001) -> ApplicationResult<VehicleRoutingSolution> {
1002 let (qubo, _var_map) = problem.to_qubo()?;
1004
1005 let ising = IsingModel::from_qubo(&qubo);
1007
1008 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 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 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]; let distance = vrp.calculate_route_distance(&route);
1065 assert_eq!(distance, 10.0 + 8.0 + 15.0); }
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]; 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]; assert!(!vrp.check_capacity_constraint(&route, 0)); let route2 = vec![0, 1, 0]; assert!(vrp.check_capacity_constraint(&route2, 0)); }
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 let invalid_vrp = VehicleRoutingProblem::new(0, 2, distance_matrix, demands, capacities);
1117 }
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); let larger_problems = create_benchmark_problems(6)
1128 .expect("Creating larger benchmark problems should succeed");
1129 assert_eq!(larger_problems.len(), 2); for problem in &larger_problems {
1132 assert!(problem.validate().is_ok());
1133 }
1134 }
1135}