use super::{
ApplicationError, ApplicationResult, IndustryConstraint, IndustryObjective, IndustrySolution,
OptimizationProblem,
};
use crate::ising::IsingModel;
use crate::qubo::{QuboBuilder, QuboFormulation};
use crate::simulator::{AnnealingParams, ClassicalAnnealingSimulator};
use std::collections::HashMap;
use std::fmt::Write;
#[derive(Debug, Clone)]
pub struct VehicleRoutingProblem {
pub num_vehicles: usize,
pub num_customers: usize,
pub distance_matrix: Vec<Vec<f64>>,
pub customer_demands: Vec<f64>,
pub vehicle_capacities: Vec<f64>,
pub time_windows: Vec<(f64, f64)>,
pub service_times: Vec<f64>,
pub max_route_duration: f64,
pub vehicle_costs: Vec<f64>,
pub customer_priorities: Vec<f64>,
}
impl VehicleRoutingProblem {
pub fn new(
num_vehicles: usize,
num_customers: usize,
distance_matrix: Vec<Vec<f64>>,
customer_demands: Vec<f64>,
vehicle_capacities: Vec<f64>,
) -> ApplicationResult<Self> {
let total_locations = num_customers + 1;
if distance_matrix.len() != total_locations {
return Err(ApplicationError::InvalidConfiguration(
"Distance matrix dimension mismatch".to_string(),
));
}
if customer_demands.len() != num_customers {
return Err(ApplicationError::InvalidConfiguration(
"Customer demands length mismatch".to_string(),
));
}
if vehicle_capacities.len() != num_vehicles {
return Err(ApplicationError::InvalidConfiguration(
"Vehicle capacities length mismatch".to_string(),
));
}
Ok(Self {
num_vehicles,
num_customers,
distance_matrix,
customer_demands,
vehicle_capacities,
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], })
}
pub fn set_time_windows(&mut self, time_windows: Vec<(f64, f64)>) -> ApplicationResult<()> {
if time_windows.len() != self.num_customers + 1 {
return Err(ApplicationError::InvalidConfiguration(
"Time windows length mismatch".to_string(),
));
}
self.time_windows = time_windows;
Ok(())
}
#[must_use]
pub fn calculate_route_distance(&self, route: &[usize]) -> f64 {
if route.len() < 2 {
return 0.0;
}
let mut total_distance = 0.0;
for window in route.windows(2) {
total_distance += self.distance_matrix[window[0]][window[1]];
}
total_distance
}
#[must_use]
pub fn check_capacity_constraint(&self, route: &[usize], vehicle_idx: usize) -> bool {
let total_demand: f64 = route.iter()
.skip(1) .take(route.len() - 2) .map(|&customer| self.customer_demands[customer - 1]) .sum();
total_demand <= self.vehicle_capacities[vehicle_idx]
}
#[must_use]
pub fn check_time_windows(&self, route: &[usize]) -> bool {
let mut current_time = 0.0;
for i in 1..route.len() {
let prev_location = route[i - 1];
let current_location = route[i];
current_time += self.distance_matrix[prev_location][current_location] / 50.0;
let (earliest, latest) = self.time_windows[current_location];
if current_time > latest {
return false; }
if current_time < earliest {
current_time = earliest;
}
current_time += self.service_times[current_location];
}
current_time <= self.max_route_duration
}
}
impl OptimizationProblem for VehicleRoutingProblem {
type Solution = VehicleRoutingSolution;
type ObjectiveValue = f64;
fn description(&self) -> String {
format!(
"Vehicle routing problem with {} vehicles and {} customers",
self.num_vehicles, self.num_customers
)
}
fn size_metrics(&self) -> HashMap<String, usize> {
let mut metrics = HashMap::new();
metrics.insert("num_vehicles".to_string(), self.num_vehicles);
metrics.insert("num_customers".to_string(), self.num_customers);
metrics.insert("total_locations".to_string(), self.num_customers + 1);
metrics
}
fn validate(&self) -> ApplicationResult<()> {
if self.num_vehicles == 0 {
return Err(ApplicationError::DataValidationError(
"At least one vehicle required".to_string(),
));
}
if self.num_customers == 0 {
return Err(ApplicationError::DataValidationError(
"At least one customer required".to_string(),
));
}
for &demand in &self.customer_demands {
if demand < 0.0 {
return Err(ApplicationError::DataValidationError(
"Customer demands must be non-negative".to_string(),
));
}
}
for &capacity in &self.vehicle_capacities {
if capacity <= 0.0 {
return Err(ApplicationError::DataValidationError(
"Vehicle capacities must be positive".to_string(),
));
}
}
Ok(())
}
fn to_qubo(&self) -> ApplicationResult<(crate::ising::QuboModel, HashMap<String, usize>)> {
let mut builder = QuboBuilder::new();
let total_locations = self.num_customers + 1;
let num_vars = total_locations * total_locations * self.num_vehicles;
let mut string_var_map = HashMap::new();
let var_index = |i: usize, j: usize, k: usize| -> usize {
i * total_locations * self.num_vehicles + j * self.num_vehicles + k
};
for i in 0..total_locations {
for j in 0..total_locations {
if i != j {
for k in 0..self.num_vehicles {
let var_idx = var_index(i, j, k);
let var_name = format!("x_{i}_{j}_{k}");
string_var_map.insert(var_name, var_idx);
}
}
}
}
for i in 0..total_locations {
for j in 0..total_locations {
if i != j {
for k in 0..self.num_vehicles {
let var_idx = var_index(i, j, k);
let cost = self.distance_matrix[i][j] * self.vehicle_costs[k];
builder.add_bias(var_idx, cost);
}
}
}
}
let visit_penalty = 1000.0;
for customer in 1..total_locations {
for i in 0..total_locations {
if i != customer {
for k1 in 0..self.num_vehicles {
let var1 = var_index(i, customer, k1);
builder.add_bias(var1, -visit_penalty);
for j in 0..total_locations {
if j != customer {
for k2 in 0..self.num_vehicles {
if (i, k1) != (j, k2) {
let var2 = var_index(j, customer, k2);
builder.add_coupling(var1, var2, visit_penalty);
}
}
}
}
}
}
}
}
let flow_penalty = 800.0;
for location in 0..total_locations {
for k in 0..self.num_vehicles {
let mut in_vars = Vec::new();
let mut out_vars = Vec::new();
for i in 0..total_locations {
if i != location {
in_vars.push(var_index(i, location, k));
}
}
for j in 0..total_locations {
if j != location {
out_vars.push(var_index(location, j, k));
}
}
for &in_var in &in_vars {
for &out_var in &out_vars {
builder.add_coupling(in_var, out_var, -flow_penalty);
}
}
}
}
let capacity_penalty = 500.0;
for k in 0..self.num_vehicles {
for customer in 1..total_locations {
let demand = self.customer_demands[customer - 1];
let capacity_violation = (demand / self.vehicle_capacities[k]).max(0.0);
for i in 0..total_locations {
if i != customer {
let var_idx = var_index(i, customer, k);
builder.add_bias(var_idx, capacity_penalty * capacity_violation);
}
}
}
}
Ok((builder.build(), string_var_map))
}
fn evaluate_solution(
&self,
solution: &Self::Solution,
) -> ApplicationResult<Self::ObjectiveValue> {
let mut total_cost = 0.0;
for (vehicle_idx, route) in solution.routes.iter().enumerate() {
if !route.is_empty() {
let distance = self.calculate_route_distance(route);
total_cost += distance * self.vehicle_costs[vehicle_idx];
if !self.check_capacity_constraint(route, vehicle_idx) {
total_cost += 10_000.0; }
if !self.check_time_windows(route) {
total_cost += 5000.0; }
}
}
Ok(total_cost)
}
fn is_feasible(&self, solution: &Self::Solution) -> bool {
let mut visited_customers = vec![false; self.num_customers];
for route in &solution.routes {
for &location in route {
if location > 0 && location <= self.num_customers {
if visited_customers[location - 1] {
return false; }
visited_customers[location - 1] = true;
}
}
}
if !visited_customers.iter().all(|&visited| visited) {
return false;
}
for (vehicle_idx, route) in solution.routes.iter().enumerate() {
if !route.is_empty() {
if !self.check_capacity_constraint(route, vehicle_idx) {
return false;
}
if !self.check_time_windows(route) {
return false;
}
}
}
true
}
}
#[derive(Debug, Clone)]
pub struct VehicleRoutingSolution {
pub routes: Vec<Vec<usize>>,
pub total_distance: f64,
pub total_cost: f64,
pub route_stats: Vec<RouteStatistics>,
pub metrics: TransportationMetrics,
}
#[derive(Debug, Clone)]
pub struct RouteStatistics {
pub distance: f64,
pub duration: f64,
pub total_demand: f64,
pub num_customers: usize,
pub capacity_utilization: f64,
pub time_window_compliance: bool,
}
#[derive(Debug, Clone)]
pub struct TransportationMetrics {
pub fleet_utilization: f64,
pub avg_route_distance: f64,
pub avg_route_duration: f64,
pub fuel_consumption: f64,
pub co2_emissions: f64,
pub service_level: f64,
pub cost_per_customer: f64,
}
impl IndustrySolution for VehicleRoutingSolution {
type Problem = VehicleRoutingProblem;
fn from_binary(problem: &Self::Problem, binary_solution: &[i8]) -> ApplicationResult<Self> {
let total_locations = problem.num_customers + 1;
let num_vars = total_locations * total_locations * problem.num_vehicles;
let mut routes = vec![Vec::new(); problem.num_vehicles];
let var_index = |i: usize, j: usize, k: usize| -> usize {
i * total_locations * problem.num_vehicles + j * total_locations + k
};
for k in 0..problem.num_vehicles {
let mut current_location = 0; let mut route = vec![0]; let mut visited = vec![false; total_locations];
visited[0] = true;
while route.len() < total_locations && route.len() < 10 {
let mut next_location = None;
for j in 0..total_locations {
if !visited[j] {
let var_idx = var_index(current_location, j, k);
if var_idx < binary_solution.len() && binary_solution[var_idx] > 0 {
next_location = Some(j);
break;
}
}
}
if let Some(next) = next_location {
route.push(next);
visited[next] = true;
current_location = next;
} else {
break;
}
}
if route.len() > 1 {
route.push(0);
}
routes[k] = route;
}
let mut total_distance = 0.0;
let mut route_stats = Vec::new();
let mut vehicles_used = 0;
for (vehicle_idx, route) in routes.iter().enumerate() {
if route.len() > 2 {
vehicles_used += 1;
let distance = problem.calculate_route_distance(route);
total_distance += distance;
let total_demand: f64 = route
.iter()
.skip(1)
.take(route.len() - 2)
.map(|&customer| problem.customer_demands[customer - 1])
.sum();
let capacity_utilization = total_demand / problem.vehicle_capacities[vehicle_idx];
let duration = (route.len() as f64).mul_add(5.0, distance / 50.0);
route_stats.push(RouteStatistics {
distance,
duration,
total_demand,
num_customers: route.len().saturating_sub(2),
capacity_utilization,
time_window_compliance: problem.check_time_windows(route),
});
} else {
route_stats.push(RouteStatistics {
distance: 0.0,
duration: 0.0,
total_demand: 0.0,
num_customers: 0,
capacity_utilization: 0.0,
time_window_compliance: true,
});
}
}
let total_cost =
((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;
let avg_route_distance = if vehicles_used > 0 {
total_distance / vehicles_used as f64
} else {
0.0
};
let avg_route_duration = route_stats
.iter()
.filter(|s| s.num_customers > 0)
.map(|s| s.duration)
.sum::<f64>()
/ vehicles_used.max(1) as f64;
let fuel_consumption = total_distance * 0.1; let co2_emissions = fuel_consumption * 2.3;
let customers_on_time = route_stats
.iter()
.filter(|s| s.time_window_compliance)
.map(|s| s.num_customers)
.sum::<usize>();
let service_level = customers_on_time as f64 / problem.num_customers as f64;
let cost_per_customer = if problem.num_customers > 0 {
total_cost / problem.num_customers as f64
} else {
0.0
};
let metrics = TransportationMetrics {
fleet_utilization,
avg_route_distance,
avg_route_duration,
fuel_consumption,
co2_emissions,
service_level,
cost_per_customer,
};
Ok(Self {
routes,
total_distance,
total_cost,
route_stats,
metrics,
})
}
fn summary(&self) -> HashMap<String, String> {
let mut summary = HashMap::new();
summary.insert("type".to_string(), "Vehicle Routing".to_string());
summary.insert(
"total_distance".to_string(),
format!("{:.1} km", self.total_distance),
);
summary.insert("total_cost".to_string(), format!("${:.2}", self.total_cost));
summary.insert(
"fleet_utilization".to_string(),
format!("{:.1}%", self.metrics.fleet_utilization * 100.0),
);
summary.insert(
"service_level".to_string(),
format!("{:.1}%", self.metrics.service_level * 100.0),
);
summary.insert(
"fuel_consumption".to_string(),
format!("{:.1} L", self.metrics.fuel_consumption),
);
summary.insert(
"co2_emissions".to_string(),
format!("{:.1} kg", self.metrics.co2_emissions),
);
let vehicles_used = self.routes.iter().filter(|r| r.len() > 2).count();
summary.insert("vehicles_used".to_string(), vehicles_used.to_string());
summary
}
fn metrics(&self) -> HashMap<String, f64> {
let mut metrics = HashMap::new();
metrics.insert("total_distance".to_string(), self.total_distance);
metrics.insert("total_cost".to_string(), self.total_cost);
metrics.insert(
"fleet_utilization".to_string(),
self.metrics.fleet_utilization,
);
metrics.insert(
"avg_route_distance".to_string(),
self.metrics.avg_route_distance,
);
metrics.insert(
"avg_route_duration".to_string(),
self.metrics.avg_route_duration,
);
metrics.insert(
"fuel_consumption".to_string(),
self.metrics.fuel_consumption,
);
metrics.insert("co2_emissions".to_string(), self.metrics.co2_emissions);
metrics.insert("service_level".to_string(), self.metrics.service_level);
metrics.insert(
"cost_per_customer".to_string(),
self.metrics.cost_per_customer,
);
let vehicles_used = self.routes.iter().filter(|r| r.len() > 2).count();
metrics.insert("vehicles_used".to_string(), vehicles_used as f64);
metrics
}
fn export_format(&self) -> ApplicationResult<String> {
let mut output = String::new();
output.push_str("# Vehicle Routing Optimization Report\n\n");
output.push_str("## Summary\n");
writeln!(output, "Total Distance: {:.1} km", self.total_distance)
.expect("Writing to String should not fail");
writeln!(output, "Total Cost: ${:.2}", self.total_cost)
.expect("Writing to String should not fail");
write!(
output,
"Fleet Utilization: {:.1}%\n",
self.metrics.fleet_utilization * 100.0
)
.expect("Writing to String should not fail");
write!(
output,
"Service Level: {:.1}%\n",
self.metrics.service_level * 100.0
)
.expect("Writing to String should not fail");
output.push_str("\n## Environmental Impact\n");
write!(
output,
"Fuel Consumption: {:.1} L\n",
self.metrics.fuel_consumption
)
.expect("Writing to String should not fail");
write!(
output,
"CO2 Emissions: {:.1} kg\n",
self.metrics.co2_emissions
)
.expect("Writing to String should not fail");
output.push_str("\n## Route Details\n");
for (i, (route, stats)) in self.routes.iter().zip(&self.route_stats).enumerate() {
if route.len() > 2 {
write!(output, "Vehicle {}: ", i + 1).expect("Writing to String should not fail");
write!(output, "Route {route:?}, ").expect("Writing to String should not fail");
write!(output, "Distance: {:.1} km, ", stats.distance)
.expect("Writing to String should not fail");
write!(output, "Duration: {:.1} h, ", stats.duration / 60.0)
.expect("Writing to String should not fail");
write!(output, "Customers: {}, ", stats.num_customers)
.expect("Writing to String should not fail");
write!(
output,
"Capacity: {:.1}%\n",
stats.capacity_utilization * 100.0
)
.expect("Writing to String should not fail");
}
}
Ok(output)
}
}
#[derive(Debug, Clone)]
pub struct TrafficFlowOptimization {
pub num_intersections: usize,
pub num_segments: usize,
pub flow_matrix: Vec<Vec<f64>>,
pub segment_capacities: Vec<f64>,
pub cycle_times: Vec<f64>,
pub green_time_allocations: Vec<Vec<f64>>,
pub priority_routes: Vec<PriorityRoute>,
pub emission_limits: Vec<f64>,
}
#[derive(Debug, Clone)]
pub struct PriorityRoute {
pub path: Vec<usize>,
pub priority: usize,
pub max_delay: f64,
pub route_type: RouteType,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RouteType {
Emergency,
PublicTransport,
Commercial,
Regular,
}
impl TrafficFlowOptimization {
pub fn new(
num_intersections: usize,
num_segments: usize,
flow_matrix: Vec<Vec<f64>>,
segment_capacities: Vec<f64>,
) -> ApplicationResult<Self> {
if flow_matrix.len() != num_intersections {
return Err(ApplicationError::InvalidConfiguration(
"Flow matrix dimension mismatch".to_string(),
));
}
if segment_capacities.len() != num_segments {
return Err(ApplicationError::InvalidConfiguration(
"Segment capacities length mismatch".to_string(),
));
}
Ok(Self {
num_intersections,
num_segments,
flow_matrix,
segment_capacities,
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(),
emission_limits: vec![100.0; num_intersections], })
}
pub fn add_priority_route(&mut self, route: PriorityRoute) {
self.priority_routes.push(route);
}
#[must_use]
pub fn calculate_total_travel_time(&self, solution: &TrafficFlowSolution) -> f64 {
let mut total_time = 0.0;
for i in 0..self.num_intersections {
for j in 0..self.num_intersections {
if i != j && self.flow_matrix[i][j] > 0.0 {
let flow = self.flow_matrix[i][j];
let delay = self.calculate_intersection_delay(i, &solution.signal_timings[i]);
total_time += flow * delay;
}
}
}
total_time
}
fn calculate_intersection_delay(
&self,
intersection: usize,
timing: &IntersectionTiming,
) -> f64 {
let cycle_time = timing.cycle_time;
let effective_green = timing.green_times.iter().sum::<f64>();
let flow_ratio = 0.8;
if effective_green <= 0.0 {
return 1000.0; }
let delay = (cycle_time * (1.0 - effective_green / cycle_time).powi(2))
/ (2.0 * (1.0 - flow_ratio * effective_green / cycle_time));
delay.clamp(0.0, 1000.0) }
}
#[derive(Debug, Clone)]
pub struct TrafficFlowSolution {
pub signal_timings: Vec<IntersectionTiming>,
pub total_travel_time: f64,
pub average_delay: f64,
pub throughput_metrics: ThroughputMetrics,
pub environmental_impact: EnvironmentalImpact,
}
#[derive(Debug, Clone)]
pub struct IntersectionTiming {
pub cycle_time: f64,
pub green_times: Vec<f64>,
pub offset: f64,
pub coordination_factor: f64,
}
#[derive(Debug, Clone)]
pub struct ThroughputMetrics {
pub vehicles_per_hour: f64,
pub average_speed: f64,
pub capacity_utilization: f64,
pub average_queue_length: f64,
pub level_of_service: String,
}
#[derive(Debug, Clone)]
pub struct EnvironmentalImpact {
pub co2_emissions: f64,
pub nox_emissions: f64,
pub fuel_consumption: f64,
pub noise_level: f64,
}
#[derive(Debug, Clone)]
pub struct BinaryVehicleRoutingProblem {
inner: VehicleRoutingProblem,
}
impl BinaryVehicleRoutingProblem {
#[must_use]
pub const fn new(inner: VehicleRoutingProblem) -> Self {
Self { inner }
}
}
impl OptimizationProblem for BinaryVehicleRoutingProblem {
type Solution = Vec<i8>;
type ObjectiveValue = f64;
fn description(&self) -> String {
self.inner.description()
}
fn size_metrics(&self) -> HashMap<String, usize> {
self.inner.size_metrics()
}
fn validate(&self) -> ApplicationResult<()> {
self.inner.validate()
}
fn to_qubo(&self) -> ApplicationResult<(crate::ising::QuboModel, HashMap<String, usize>)> {
self.inner.to_qubo()
}
fn evaluate_solution(
&self,
solution: &Self::Solution,
) -> ApplicationResult<Self::ObjectiveValue> {
let routing_solution = VehicleRoutingSolution::from_binary(&self.inner, solution)?;
self.inner.evaluate_solution(&routing_solution)
}
fn is_feasible(&self, solution: &Self::Solution) -> bool {
if let Ok(routing_solution) = VehicleRoutingSolution::from_binary(&self.inner, solution) {
self.inner.is_feasible(&routing_solution)
} else {
false
}
}
}
pub fn create_benchmark_problems(
size: usize,
) -> ApplicationResult<Vec<Box<dyn OptimizationProblem<Solution = Vec<i8>, ObjectiveValue = f64>>>>
{
let mut problems = Vec::new();
let distance_matrix = vec![
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], ];
let customer_demands = vec![5.0, 8.0, 3.0];
let vehicle_capacities = vec![15.0, 12.0];
let vrp = VehicleRoutingProblem::new(
2, 3, distance_matrix,
customer_demands,
vehicle_capacities,
)?;
problems.push(Box::new(BinaryVehicleRoutingProblem::new(vrp))
as Box<
dyn OptimizationProblem<Solution = Vec<i8>, ObjectiveValue = f64>,
>);
if size >= 6 {
let mut large_distance_matrix = vec![vec![0.0; size + 1]; size + 1];
for i in 0..=size {
for j in 0..=size {
if i != j {
large_distance_matrix[i][j] = (i as f64 - j as f64).abs().mul_add(5.0, 10.0);
}
}
}
let large_demands = vec![5.0; size];
let large_capacities = vec![20.0; size / 2];
let large_vrp = VehicleRoutingProblem::new(
size / 2,
size,
large_distance_matrix,
large_demands,
large_capacities,
)?;
problems.push(Box::new(BinaryVehicleRoutingProblem::new(large_vrp))
as Box<
dyn OptimizationProblem<Solution = Vec<i8>, ObjectiveValue = f64>,
>);
}
Ok(problems)
}
pub fn solve_vehicle_routing(
problem: &VehicleRoutingProblem,
params: Option<AnnealingParams>,
) -> ApplicationResult<VehicleRoutingSolution> {
let (qubo, _var_map) = problem.to_qubo()?;
let ising = IsingModel::from_qubo(&qubo);
let annealing_params = params.unwrap_or_else(|| {
let mut p = AnnealingParams::default();
p.num_sweeps = 30_000;
p.num_repetitions = 50;
p.initial_temperature = 8.0;
p.final_temperature = 0.001;
p
});
let simulator = ClassicalAnnealingSimulator::new(annealing_params)
.map_err(|e| ApplicationError::OptimizationError(e.to_string()))?;
let result = simulator
.solve(&ising)
.map_err(|e| ApplicationError::OptimizationError(e.to_string()))?;
VehicleRoutingSolution::from_binary(problem, &result.best_spins)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vehicle_routing_creation() {
let distance_matrix = vec![
vec![0.0, 10.0, 15.0],
vec![10.0, 0.0, 8.0],
vec![15.0, 8.0, 0.0],
];
let demands = vec![5.0, 8.0];
let capacities = vec![15.0];
let vrp = VehicleRoutingProblem::new(1, 2, distance_matrix, demands, capacities)
.expect("VehicleRoutingProblem creation should succeed");
assert_eq!(vrp.num_vehicles, 1);
assert_eq!(vrp.num_customers, 2);
}
#[test]
fn test_route_distance_calculation() {
let distance_matrix = vec![
vec![0.0, 10.0, 15.0],
vec![10.0, 0.0, 8.0],
vec![15.0, 8.0, 0.0],
];
let demands = vec![5.0, 8.0];
let capacities = vec![15.0];
let vrp = VehicleRoutingProblem::new(1, 2, distance_matrix, demands, capacities)
.expect("VehicleRoutingProblem creation should succeed");
let route = vec![0, 1, 2, 0]; let distance = vrp.calculate_route_distance(&route);
assert_eq!(distance, 10.0 + 8.0 + 15.0); }
#[test]
fn test_capacity_constraint() {
let distance_matrix = vec![vec![0.0; 3]; 3];
let demands = vec![5.0, 8.0];
let capacities = vec![10.0];
let vrp = VehicleRoutingProblem::new(1, 2, distance_matrix, demands, capacities)
.expect("VehicleRoutingProblem creation should succeed");
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)); }
#[test]
fn test_traffic_flow_creation() {
let flow_matrix = vec![
vec![0.0, 100.0, 50.0],
vec![80.0, 0.0, 120.0],
vec![60.0, 90.0, 0.0],
];
let capacities = vec![200.0, 180.0, 150.0];
let traffic = TrafficFlowOptimization::new(3, 3, flow_matrix, capacities)
.expect("TrafficFlowOptimization creation should succeed");
assert_eq!(traffic.num_intersections, 3);
assert_eq!(traffic.num_segments, 3);
}
#[test]
fn test_vrp_validation() {
let distance_matrix = vec![vec![0.0; 3]; 3];
let demands = vec![5.0, 8.0];
let capacities = vec![15.0];
let vrp = VehicleRoutingProblem::new(
1,
2,
distance_matrix.clone(),
demands.clone(),
capacities.clone(),
)
.expect("VehicleRoutingProblem creation should succeed");
assert!(vrp.validate().is_ok());
let invalid_vrp = VehicleRoutingProblem::new(0, 2, distance_matrix, demands, capacities);
}
#[test]
fn test_benchmark_problems() {
let problems =
create_benchmark_problems(4).expect("Creating benchmark problems should succeed");
assert_eq!(problems.len(), 1);
let larger_problems = create_benchmark_problems(6)
.expect("Creating larger benchmark problems should succeed");
assert_eq!(larger_problems.len(), 2);
for problem in &larger_problems {
assert!(problem.validate().is_ok());
}
}
}