Skip to main content

quantrs2_tytan/applications/logistics/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use scirs2_core::ndarray::{Array1, Array2};
6use scirs2_core::random::prelude::*;
7use std::collections::{HashMap, HashSet};
8
9#[derive(Debug, Clone)]
10pub struct Route {
11    pub vehicle_id: usize,
12    pub path: Vec<usize>,
13    pub total_distance: f64,
14    pub total_demand: f64,
15    pub arrival_times: Vec<f64>,
16}
17/// Supply chain optimizer
18pub struct SupplyChainOptimizer {
19    /// Network structure
20    network: SupplyChainNetwork,
21    /// Optimization objectives
22    objectives: Vec<SupplyChainObjective>,
23    /// Constraints
24    constraints: SupplyChainConstraints,
25    /// Time horizon
26    time_horizon: usize,
27}
28impl SupplyChainOptimizer {
29    /// Create new supply chain optimizer
30    pub fn new(network: SupplyChainNetwork, time_horizon: usize) -> Self {
31        Self {
32            network,
33            objectives: vec![SupplyChainObjective::MinimizeCost],
34            constraints: SupplyChainConstraints {
35                enforce_capacity: true,
36                min_service_level: 0.95,
37                max_lead_time: None,
38                safety_stock: HashMap::new(),
39                max_budget: None,
40            },
41            time_horizon,
42        }
43    }
44    /// Add objective
45    pub fn add_objective(mut self, objective: SupplyChainObjective) -> Self {
46        self.objectives.push(objective);
47        self
48    }
49    /// Set constraints
50    pub fn with_constraints(mut self, constraints: SupplyChainConstraints) -> Self {
51        self.constraints = constraints;
52        self
53    }
54    /// Build QUBO formulation
55    pub fn build_qubo(&self) -> Result<(Array2<f64>, HashMap<String, usize>), String> {
56        let mut var_map = HashMap::new();
57        let mut var_idx = 0;
58        for t in 0..self.time_horizon {
59            for s in &self.network.suppliers {
60                for w in &self.network.warehouses {
61                    let var_name = format!("x_{}_{}_{}", s.id, w.id, t);
62                    var_map.insert(var_name, var_idx);
63                    var_idx += 1;
64                }
65            }
66            for w in &self.network.warehouses {
67                for d in &self.network.distribution_centers {
68                    let var_name = format!("y_{}_{}_{}", w.id, d.id, t);
69                    var_map.insert(var_name, var_idx);
70                    var_idx += 1;
71                }
72            }
73            for d in &self.network.distribution_centers {
74                for c in &self.network.customers {
75                    let var_name = format!("z_{}_{}_{}", d.id, c.id, t);
76                    var_map.insert(var_name, var_idx);
77                    var_idx += 1;
78                }
79            }
80            for w in &self.network.warehouses {
81                let var_name = format!("I_{}_{}", w.id, t);
82                var_map.insert(var_name, var_idx);
83                var_idx += 1;
84            }
85        }
86        let n_vars = var_idx;
87        let mut qubo = Array2::zeros((n_vars, n_vars));
88        for objective in &self.objectives {
89            match objective {
90                SupplyChainObjective::MinimizeCost => {
91                    self.add_cost_objective(&mut qubo, &var_map)?;
92                }
93                SupplyChainObjective::MinimizeInventory => {
94                    self.add_inventory_objective(&mut qubo, &var_map)?;
95                }
96                _ => {}
97            }
98        }
99        self.add_flow_conservation_constraints(&mut qubo, &var_map)?;
100        self.add_capacity_constraints_sc(&mut qubo, &var_map)?;
101        self.add_demand_constraints(&mut qubo, &var_map)?;
102        Ok((qubo, var_map))
103    }
104    /// Add cost objective
105    fn add_cost_objective(
106        &self,
107        qubo: &mut Array2<f64>,
108        var_map: &HashMap<String, usize>,
109    ) -> Result<(), String> {
110        for link in &self.network.links {
111            for t in 0..self.time_horizon {
112                let var_name = match (&link.from_type, &link.to_type) {
113                    (NodeType::Supplier, NodeType::Warehouse) => {
114                        format!("x_{}_{}_{}", link.from_id, link.to_id, t)
115                    }
116                    (NodeType::Warehouse, NodeType::DistributionCenter) => {
117                        format!("y_{}_{}_{}", link.from_id, link.to_id, t)
118                    }
119                    (NodeType::DistributionCenter, NodeType::Customer) => {
120                        format!("z_{}_{}_{}", link.from_id, link.to_id, t)
121                    }
122                    _ => continue,
123                };
124                if let Some(&idx) = var_map.get(&var_name) {
125                    qubo[[idx, idx]] += link.cost_per_unit;
126                }
127            }
128        }
129        for w in &self.network.warehouses {
130            for t in 0..self.time_horizon {
131                let var_name = format!("I_{}_{}", w.id, t);
132                if let Some(&idx) = var_map.get(&var_name) {
133                    qubo[[idx, idx]] += w.holding_cost;
134                }
135            }
136        }
137        Ok(())
138    }
139    /// Add inventory objective
140    fn add_inventory_objective(
141        &self,
142        qubo: &mut Array2<f64>,
143        var_map: &HashMap<String, usize>,
144    ) -> Result<(), String> {
145        for w in &self.network.warehouses {
146            for t in 0..self.time_horizon {
147                let var_name = format!("I_{}_{}", w.id, t);
148                if let Some(&idx) = var_map.get(&var_name) {
149                    qubo[[idx, idx]] += 1.0;
150                }
151            }
152        }
153        Ok(())
154    }
155    /// Add flow conservation constraints
156    fn add_flow_conservation_constraints(
157        &self,
158        _qubo: &mut Array2<f64>,
159        _var_map: &HashMap<String, usize>,
160    ) -> Result<(), String> {
161        let _penalty = 1000.0;
162        for _w in &self.network.warehouses {
163            for _t in 1..self.time_horizon {}
164        }
165        Ok(())
166    }
167    /// Add capacity constraints
168    fn add_capacity_constraints_sc(
169        &self,
170        qubo: &mut Array2<f64>,
171        var_map: &HashMap<String, usize>,
172    ) -> Result<(), String> {
173        let penalty = 1000.0;
174        for s in &self.network.suppliers {
175            for t in 0..self.time_horizon {
176                for w in &self.network.warehouses {
177                    let var_name = format!("x_{}_{}_{}", s.id, w.id, t);
178                    if let Some(&idx) = var_map.get(&var_name) {
179                        if s.capacity > 0.0 {
180                            qubo[[idx, idx]] += penalty / s.capacity;
181                        }
182                    }
183                }
184            }
185        }
186        Ok(())
187    }
188    /// Add demand constraints
189    fn add_demand_constraints(
190        &self,
191        qubo: &mut Array2<f64>,
192        var_map: &HashMap<String, usize>,
193    ) -> Result<(), String> {
194        let penalty = 1000.0;
195        for c in &self.network.customers {
196            for t in 0..self.time_horizon.min(c.demand.len()) {
197                for d in &self.network.distribution_centers {
198                    let var_name = format!("z_{}_{}_{}", d.id, c.id, t);
199                    if let Some(&idx) = var_map.get(&var_name) {
200                        qubo[[idx, idx]] -= penalty * c.demand[t] * c.priority;
201                    }
202                }
203            }
204        }
205        Ok(())
206    }
207}
208#[derive(Debug, Clone)]
209pub struct DistributionCenter {
210    pub id: usize,
211    pub capacity: f64,
212    pub processing_cost: f64,
213    pub location: (f64, f64),
214}
215#[derive(Debug, Clone)]
216pub struct StorageLocation {
217    pub id: usize,
218    pub position: (usize, usize, usize),
219    pub capacity: f64,
220    pub item_type: Option<String>,
221    pub accessibility: f64,
222}
223#[derive(Debug, Clone)]
224pub struct WarehouseLayout {
225    /// Grid dimensions
226    pub rows: usize,
227    pub cols: usize,
228    pub levels: usize,
229    /// Storage locations
230    pub locations: Vec<StorageLocation>,
231    /// Picking stations
232    pub picking_stations: Vec<(usize, usize)>,
233    /// Distance function
234    pub distance_type: DistanceType,
235}
236#[derive(Debug, Clone)]
237pub struct Warehouse {
238    pub id: usize,
239    pub capacity: f64,
240    pub holding_cost: f64,
241    pub fixed_cost: f64,
242    pub location: (f64, f64),
243}
244#[derive(Debug, Clone)]
245pub struct OrderItem {
246    pub sku: String,
247    pub quantity: usize,
248    pub location: Option<usize>,
249}
250#[derive(Debug, Clone)]
251pub struct Order {
252    pub id: usize,
253    pub items: Vec<OrderItem>,
254    pub priority: f64,
255    pub due_time: f64,
256}
257#[derive(Debug, Clone)]
258pub struct PickingRoute {
259    pub locations: Vec<usize>,
260    pub sequence: Vec<usize>,
261    pub total_distance: f64,
262}
263/// Warehouse optimization
264pub struct WarehouseOptimizer {
265    /// Warehouse layout
266    layout: WarehouseLayout,
267    /// Storage policies
268    policies: StoragePolicies,
269    /// Order data
270    orders: Vec<Order>,
271    /// Optimization goals
272    goals: WarehouseGoals,
273}
274impl WarehouseOptimizer {
275    /// Create new warehouse optimizer
276    pub const fn new(
277        layout: WarehouseLayout,
278        policies: StoragePolicies,
279        orders: Vec<Order>,
280    ) -> Self {
281        Self {
282            layout,
283            policies,
284            orders,
285            goals: WarehouseGoals {
286                minimize_distance: true,
287                minimize_time: false,
288                balance_workload: false,
289                maximize_utilization: false,
290            },
291        }
292    }
293    /// Optimize order picking
294    pub fn optimize_picking(&self) -> Result<PickingPlan, String> {
295        match &self.policies.picking {
296            PickingPolicy::Batch { size } => self.optimize_batch_picking(*size),
297            _ => self.optimize_single_picking(),
298        }
299    }
300    /// Optimize batch picking
301    fn optimize_batch_picking(&self, batch_size: usize) -> Result<PickingPlan, String> {
302        let mut batches = Vec::new();
303        let mut remaining_orders = self.orders.clone();
304        while !remaining_orders.is_empty() {
305            let batch_orders: Vec<_> = remaining_orders
306                .drain(..batch_size.min(remaining_orders.len()))
307                .collect();
308            let route = self.optimize_picking_route(&batch_orders)?;
309            let estimated_time = self.estimate_picking_time(&route);
310            batches.push(Batch {
311                orders: batch_orders,
312                route,
313                estimated_time,
314            });
315        }
316        let total_distance = batches.iter().map(|b| b.route.total_distance).sum();
317        let total_time = batches.iter().map(|b| b.estimated_time).sum();
318        Ok(PickingPlan {
319            batches,
320            total_distance,
321            total_time,
322        })
323    }
324    /// Optimize single order picking
325    fn optimize_single_picking(&self) -> Result<PickingPlan, String> {
326        let mut batches = Vec::new();
327        for order in &self.orders {
328            let route = self.optimize_picking_route(&[order.clone()])?;
329            let estimated_time = self.estimate_picking_time(&route);
330            batches.push(Batch {
331                orders: vec![order.clone()],
332                route,
333                estimated_time,
334            });
335        }
336        let total_distance = batches.iter().map(|b| b.route.total_distance).sum();
337        let total_time = batches.iter().map(|b| b.estimated_time).sum();
338        Ok(PickingPlan {
339            batches,
340            total_distance,
341            total_time,
342        })
343    }
344    /// Optimize picking route for orders
345    fn optimize_picking_route(&self, orders: &[Order]) -> Result<PickingRoute, String> {
346        let mut pick_locations = Vec::new();
347        for order in orders {
348            for item in &order.items {
349                if let Some(loc) = item.location {
350                    pick_locations.push(loc);
351                }
352            }
353        }
354        pick_locations.sort_unstable();
355        pick_locations.dedup();
356        let n = pick_locations.len() + 1;
357        let mut distances = Array2::zeros((n, n));
358        let station = self.layout.picking_stations[0];
359        for (i, &loc) in pick_locations.iter().enumerate() {
360            let loc_pos = self.layout.locations[loc].position;
361            distances[[0, i + 1]] = self.calculate_distance((station.0, station.1, 0), loc_pos);
362            distances[[i + 1, 0]] = distances[[0, i + 1]];
363        }
364        for (i, &loc1) in pick_locations.iter().enumerate() {
365            for (j, &loc2) in pick_locations.iter().enumerate() {
366                if i != j {
367                    let pos1 = self.layout.locations[loc1].position;
368                    let pos2 = self.layout.locations[loc2].position;
369                    distances[[i + 1, j + 1]] = self.calculate_distance(pos1, pos2);
370                }
371            }
372        }
373        let tsp = TSPOptimizer::new(distances)?;
374        let (_qubo, _var_map) = tsp.build_qubo()?;
375        let sequence = (0..pick_locations.len()).collect();
376        Ok(PickingRoute {
377            locations: pick_locations,
378            sequence,
379            total_distance: 0.0,
380        })
381    }
382    /// Calculate distance between positions
383    fn calculate_distance(&self, pos1: (usize, usize, usize), pos2: (usize, usize, usize)) -> f64 {
384        match self.layout.distance_type {
385            DistanceType::Manhattan => {
386                ((pos1.0 as i32 - pos2.0 as i32).abs()
387                    + (pos1.1 as i32 - pos2.1 as i32).abs()
388                    + (pos1.2 as i32 - pos2.2 as i32).abs()) as f64
389            }
390            DistanceType::Euclidean => (pos1.2 as f64 - pos2.2 as f64)
391                .mul_add(
392                    pos1.2 as f64 - pos2.2 as f64,
393                    (pos1.1 as f64 - pos2.1 as f64).mul_add(
394                        pos1.1 as f64 - pos2.1 as f64,
395                        (pos1.0 as f64 - pos2.0 as f64).powi(2),
396                    ),
397                )
398                .sqrt(),
399            _ => 0.0,
400        }
401    }
402    /// Estimate picking time for route
403    fn estimate_picking_time(&self, route: &PickingRoute) -> f64 {
404        let travel_time = route.total_distance / 1.0;
405        let pick_time = route.locations.len() as f64 * 10.0;
406        travel_time + pick_time
407    }
408}
409#[derive(Debug, Clone)]
410pub struct Customer {
411    pub id: usize,
412    pub demand: Array1<f64>,
413    pub priority: f64,
414    pub location: (f64, f64),
415}
416#[derive(Debug, Clone)]
417pub enum ConstraintViolation {
418    CapacityExceeded {
419        vehicle: usize,
420        demand: f64,
421        capacity: f64,
422    },
423    TimeWindowViolation {
424        location: usize,
425        arrival: f64,
426        window_end: f64,
427    },
428    CustomersNotVisited {
429        missing: usize,
430    },
431}
432#[derive(Debug, Clone)]
433pub struct TransportLink {
434    pub from_type: NodeType,
435    pub from_id: usize,
436    pub to_type: NodeType,
437    pub to_id: usize,
438    pub capacity: f64,
439    pub cost_per_unit: f64,
440    pub lead_time: usize,
441}
442#[derive(Debug, Clone)]
443pub struct ValidationResult {
444    pub is_valid: bool,
445    pub violations: Vec<ConstraintViolation>,
446    pub total_distance: f64,
447    pub num_vehicles_used: usize,
448}
449#[derive(Debug, Clone)]
450pub struct Supplier {
451    pub id: usize,
452    pub capacity: f64,
453    pub cost_per_unit: f64,
454    pub lead_time: usize,
455    pub reliability: f64,
456}
457#[derive(Debug, Clone)]
458pub struct WarehouseGoals {
459    /// Minimize picking distance
460    pub minimize_distance: bool,
461    /// Minimize order completion time
462    pub minimize_time: bool,
463    /// Balance workload
464    pub balance_workload: bool,
465    /// Maximize space utilization
466    pub maximize_utilization: bool,
467}
468#[derive(Debug, Clone)]
469pub enum ReplenishmentPolicy {
470    /// Fixed order quantity
471    FixedQuantity { quantity: f64 },
472    /// Reorder point
473    ReorderPoint { level: f64 },
474    /// Periodic review
475    Periodic { interval: usize },
476}
477/// Vehicle Routing Problem (VRP) optimizer
478pub struct VehicleRoutingOptimizer {
479    /// Distance matrix between locations
480    distance_matrix: Array2<f64>,
481    /// Vehicle capacity
482    vehicle_capacity: f64,
483    /// Demand at each location
484    demands: Array1<f64>,
485    /// Time windows for each location
486    time_windows: Option<Vec<TimeWindow>>,
487    /// Number of vehicles
488    num_vehicles: usize,
489    /// Depot location
490    depot: usize,
491    /// Problem variant
492    variant: VRPVariant,
493}
494impl VehicleRoutingOptimizer {
495    /// Create new VRP optimizer
496    pub fn new(
497        distance_matrix: Array2<f64>,
498        vehicle_capacity: f64,
499        demands: Array1<f64>,
500        num_vehicles: usize,
501    ) -> Result<Self, String> {
502        if distance_matrix.shape()[0] != distance_matrix.shape()[1] {
503            return Err("Distance matrix must be square".to_string());
504        }
505        if distance_matrix.shape()[0] != demands.len() {
506            return Err("Distance matrix and demands size mismatch".to_string());
507        }
508        Ok(Self {
509            distance_matrix,
510            vehicle_capacity,
511            demands,
512            time_windows: None,
513            num_vehicles,
514            depot: 0,
515            variant: VRPVariant::CVRP,
516        })
517    }
518    /// Set problem variant
519    pub fn with_variant(mut self, variant: VRPVariant) -> Self {
520        self.variant = variant;
521        self
522    }
523    /// Set time windows
524    pub fn with_time_windows(mut self, time_windows: Vec<TimeWindow>) -> Self {
525        self.time_windows = Some(time_windows);
526        self.variant = VRPVariant::VRPTW;
527        self
528    }
529    /// Build QUBO formulation
530    pub fn build_qubo(&self) -> Result<(Array2<f64>, HashMap<String, usize>), String> {
531        let n_locations = self.distance_matrix.shape()[0];
532        let _n_customers = n_locations - 1;
533        let n_vars = self.num_vehicles * n_locations * n_locations;
534        let mut qubo = Array2::zeros((n_vars, n_vars));
535        let mut var_map = HashMap::new();
536        let mut var_idx = 0;
537        for v in 0..self.num_vehicles {
538            for i in 0..n_locations {
539                for j in 0..n_locations {
540                    let var_name = format!("x_{v}_{i}_{j}");
541                    var_map.insert(var_name, var_idx);
542                    var_idx += 1;
543                }
544            }
545        }
546        self.add_distance_objective(&mut qubo, &var_map)?;
547        match &self.variant {
548            VRPVariant::CVRP => {
549                self.add_cvrp_constraints(&mut qubo, &var_map)?;
550            }
551            VRPVariant::VRPTW => {
552                self.add_cvrp_constraints(&mut qubo, &var_map)?;
553                self.add_time_window_constraints(&mut qubo, &var_map)?;
554            }
555            VRPVariant::MDVRP { depots } => {
556                self.add_mdvrp_constraints(&mut qubo, &var_map, depots)?;
557            }
558            _ => {
559                self.add_cvrp_constraints(&mut qubo, &var_map)?;
560            }
561        }
562        Ok((qubo, var_map))
563    }
564    /// Add distance objective
565    fn add_distance_objective(
566        &self,
567        qubo: &mut Array2<f64>,
568        var_map: &HashMap<String, usize>,
569    ) -> Result<(), String> {
570        for v in 0..self.num_vehicles {
571            for i in 0..self.distance_matrix.shape()[0] {
572                for j in 0..self.distance_matrix.shape()[1] {
573                    if i != j {
574                        let var_name = format!("x_{v}_{i}_{j}");
575                        if let Some(&var_idx) = var_map.get(&var_name) {
576                            qubo[[var_idx, var_idx]] += self.distance_matrix[[i, j]];
577                        }
578                    }
579                }
580            }
581        }
582        Ok(())
583    }
584    /// Add CVRP constraints
585    fn add_cvrp_constraints(
586        &self,
587        qubo: &mut Array2<f64>,
588        var_map: &HashMap<String, usize>,
589    ) -> Result<(), String> {
590        let penalty = 1000.0;
591        let n_locations = self.distance_matrix.shape()[0];
592        for j in 1..n_locations {
593            for v1 in 0..self.num_vehicles {
594                for i1 in 0..n_locations {
595                    if i1 != j {
596                        let var1 = format!("x_{v1}_{i1}_{j}");
597                        if let Some(&idx1) = var_map.get(&var1) {
598                            qubo[[idx1, idx1]] -= 2.0 * penalty;
599                            for v2 in 0..self.num_vehicles {
600                                for i2 in 0..n_locations {
601                                    if i2 != j {
602                                        let var2 = format!("x_{v2}_{i2}_{j}");
603                                        if let Some(&idx2) = var_map.get(&var2) {
604                                            qubo[[idx1, idx2]] += penalty;
605                                        }
606                                    }
607                                }
608                            }
609                        }
610                    }
611                }
612            }
613        }
614        for v in 0..self.num_vehicles {
615            for i in 0..n_locations {
616                for j1 in 0..n_locations {
617                    if j1 != i {
618                        let var_out = format!("x_{v}_{i}_{j1}");
619                        if let Some(&idx_out) = var_map.get(&var_out) {
620                            for j2 in 0..n_locations {
621                                if j2 != i {
622                                    let var_in = format!("x_{v}_{j2}_{i}");
623                                    if let Some(&idx_in) = var_map.get(&var_in) {
624                                        qubo[[idx_out, idx_in]] -= penalty;
625                                    }
626                                }
627                            }
628                        }
629                    }
630                }
631            }
632        }
633        self.add_capacity_constraints(qubo, var_map, penalty)?;
634        for v in 0..self.num_vehicles {
635            for j in 1..n_locations {
636                let var = format!("x_{}_{}_{}", v, 0, j);
637                if let Some(&idx) = var_map.get(&var) {
638                    qubo[[idx, idx]] -= penalty * 0.1;
639                }
640            }
641        }
642        Ok(())
643    }
644    /// Add capacity constraints
645    fn add_capacity_constraints(
646        &self,
647        qubo: &mut Array2<f64>,
648        var_map: &HashMap<String, usize>,
649        penalty: f64,
650    ) -> Result<(), String> {
651        let n_locations = self.distance_matrix.shape()[0];
652        for v in 0..self.num_vehicles {
653            let route_demand = 0.0;
654            for i in 0..n_locations {
655                for j in 1..n_locations {
656                    let var = format!("x_{v}_{i}_{j}");
657                    if let Some(&idx) = var_map.get(&var) {
658                        if route_demand + self.demands[j] > self.vehicle_capacity {
659                            qubo[[idx, idx]] += penalty * 10.0;
660                        }
661                    }
662                }
663            }
664        }
665        Ok(())
666    }
667    /// Add time window constraints
668    fn add_time_window_constraints(
669        &self,
670        qubo: &mut Array2<f64>,
671        var_map: &HashMap<String, usize>,
672    ) -> Result<(), String> {
673        if let Some(time_windows) = &self.time_windows {
674            let penalty = 1000.0;
675            let n_locations = self.distance_matrix.shape()[0];
676            for v in 0..self.num_vehicles {
677                for i in 0..n_locations {
678                    for j in 0..n_locations {
679                        if i != j {
680                            let var = format!("x_{v}_{i}_{j}");
681                            if let Some(&idx) = var_map.get(&var) {
682                                let travel_time = self.distance_matrix[[i, j]];
683                                if j < time_windows.len() {
684                                    let earliest_arrival = if i < time_windows.len() {
685                                        time_windows[i].start
686                                            + time_windows[i].service_time
687                                            + travel_time
688                                    } else {
689                                        travel_time
690                                    };
691                                    if earliest_arrival > time_windows[j].end {
692                                        qubo[[idx, idx]] += penalty * 5.0;
693                                    }
694                                }
695                            }
696                        }
697                    }
698                }
699            }
700        }
701        Ok(())
702    }
703    /// Add multi-depot constraints
704    fn add_mdvrp_constraints(
705        &self,
706        qubo: &mut Array2<f64>,
707        var_map: &HashMap<String, usize>,
708        depots: &[usize],
709    ) -> Result<(), String> {
710        let penalty = 1000.0;
711        for v in 0..self.num_vehicles {
712            for &depot in depots {
713                for j in 0..self.distance_matrix.shape()[0] {
714                    if !depots.contains(&j) {
715                        let var = format!("x_{v}_{depot}_{j}");
716                        if let Some(&idx) = var_map.get(&var) {
717                            qubo[[idx, idx]] -= penalty * 0.1;
718                        }
719                    }
720                }
721            }
722        }
723        Ok(())
724    }
725    /// Decode solution to routes
726    pub fn decode_solution(&self, solution: &HashMap<String, bool>) -> Vec<Route> {
727        let mut routes = Vec::new();
728        let n_locations = self.distance_matrix.shape()[0];
729        for v in 0..self.num_vehicles {
730            let mut route = Route {
731                vehicle_id: v,
732                path: vec![self.depot],
733                total_distance: 0.0,
734                total_demand: 0.0,
735                arrival_times: vec![0.0],
736            };
737            let mut current = self.depot;
738            let mut visited = HashSet::new();
739            visited.insert(self.depot);
740            loop {
741                let mut next_location = None;
742                for j in 0..n_locations {
743                    if !visited.contains(&j) {
744                        let var = format!("x_{v}_{current}_{j}");
745                        if *solution.get(&var).unwrap_or(&false) {
746                            next_location = Some(j);
747                            break;
748                        }
749                    }
750                }
751                if let Some(next) = next_location {
752                    route.path.push(next);
753                    route.total_distance += self.distance_matrix[[current, next]];
754                    route.total_demand += self.demands[next];
755                    let arrival_time = route.arrival_times.last().copied().unwrap_or(0.0)
756                        + self.distance_matrix[[current, next]];
757                    route.arrival_times.push(arrival_time);
758                    visited.insert(next);
759                    current = next;
760                } else {
761                    break;
762                }
763            }
764            if route.path.len() > 1 {
765                route.path.push(self.depot);
766                route.total_distance += self.distance_matrix[[current, self.depot]];
767                route.arrival_times.push(
768                    route.arrival_times.last().copied().unwrap_or(0.0)
769                        + self.distance_matrix[[current, self.depot]],
770                );
771                routes.push(route);
772            }
773        }
774        routes
775    }
776    /// Validate solution
777    pub fn validate_solution(&self, routes: &[Route]) -> ValidationResult {
778        let mut violations = Vec::new();
779        let mut visited_customers = HashSet::new();
780        for route in routes {
781            if route.total_demand > self.vehicle_capacity {
782                violations.push(ConstraintViolation::CapacityExceeded {
783                    vehicle: route.vehicle_id,
784                    demand: route.total_demand,
785                    capacity: self.vehicle_capacity,
786                });
787            }
788            if let Some(time_windows) = &self.time_windows {
789                for (i, &loc) in route.path.iter().enumerate() {
790                    if loc < time_windows.len()
791                        && i < route.arrival_times.len()
792                        && route.arrival_times[i] > time_windows[loc].end
793                    {
794                        violations.push(ConstraintViolation::TimeWindowViolation {
795                            location: loc,
796                            arrival: route.arrival_times[i],
797                            window_end: time_windows[loc].end,
798                        });
799                    }
800                }
801            }
802            for &loc in &route.path {
803                if loc != self.depot {
804                    visited_customers.insert(loc);
805                }
806            }
807        }
808        let n_customers = self.distance_matrix.shape()[0] - 1;
809        if visited_customers.len() < n_customers {
810            violations.push(ConstraintViolation::CustomersNotVisited {
811                missing: n_customers - visited_customers.len(),
812            });
813        }
814        ValidationResult {
815            is_valid: violations.is_empty(),
816            violations,
817            total_distance: routes.iter().map(|r| r.total_distance).sum(),
818            num_vehicles_used: routes.len(),
819        }
820    }
821}
822#[derive(Debug, Clone)]
823pub struct PickingPlan {
824    pub batches: Vec<Batch>,
825    pub total_distance: f64,
826    pub total_time: f64,
827}
828#[derive(Debug, Clone)]
829pub struct SupplyChainConstraints {
830    /// Capacity constraints
831    pub enforce_capacity: bool,
832    /// Service level requirements
833    pub min_service_level: f64,
834    /// Maximum lead time
835    pub max_lead_time: Option<usize>,
836    /// Safety stock requirements
837    pub safety_stock: HashMap<usize, f64>,
838    /// Budget constraint
839    pub max_budget: Option<f64>,
840}
841#[derive(Debug, Clone)]
842pub struct Batch {
843    pub orders: Vec<Order>,
844    pub route: PickingRoute,
845    pub estimated_time: f64,
846}
847#[derive(Debug, Clone)]
848pub enum DistanceType {
849    Manhattan,
850    Euclidean,
851    Rectilinear,
852    Custom,
853}
854#[derive(Debug, Clone)]
855pub struct VehicleType {
856    /// Vehicle capacity
857    capacity: f64,
858    /// Fixed cost
859    fixed_cost: f64,
860    /// Cost per distance
861    distance_cost: f64,
862    /// Maximum distance
863    max_distance: Option<f64>,
864    /// Speed factor
865    speed_factor: f64,
866}
867#[derive(Debug, Clone)]
868pub struct StoragePolicies {
869    /// Storage assignment policy
870    pub assignment: AssignmentPolicy,
871    /// Replenishment policy
872    pub replenishment: ReplenishmentPolicy,
873    /// Picking policy
874    pub picking: PickingPolicy,
875}
876#[derive(Debug, Clone)]
877pub enum TSPVariant {
878    /// Standard TSP
879    Standard,
880    /// Asymmetric TSP
881    ATSP,
882    /// TSP with time windows
883    TSPTW { time_windows: Vec<TimeWindow> },
884    /// Multiple TSP
885    MTSP { num_salesmen: usize },
886    /// Prize-collecting TSP
887    PCTSP { prizes: Vec<f64>, min_prize: f64 },
888}
889#[derive(Debug, Clone)]
890pub enum VRPVariant {
891    /// Capacitated VRP
892    CVRP,
893    /// VRP with Time Windows
894    VRPTW,
895    /// Multi-Depot VRP
896    MDVRP { depots: Vec<usize> },
897    /// Pickup and Delivery
898    VRPPD {
899        pickups: Vec<usize>,
900        deliveries: Vec<usize>,
901    },
902    /// VRP with Backhauls
903    VRPB { backhaul_customers: Vec<usize> },
904    /// Heterogeneous Fleet VRP
905    HVRP { vehicle_types: Vec<VehicleType> },
906}
907/// Traveling Salesman Problem (TSP) optimizer
908pub struct TSPOptimizer {
909    /// Distance matrix
910    distance_matrix: Array2<f64>,
911    /// Problem variant
912    variant: TSPVariant,
913    /// Subtour elimination method
914    subtour_method: SubtourElimination,
915}
916impl TSPOptimizer {
917    /// Create new TSP optimizer
918    pub fn new(distance_matrix: Array2<f64>) -> Result<Self, String> {
919        if distance_matrix.shape()[0] != distance_matrix.shape()[1] {
920            return Err("Distance matrix must be square".to_string());
921        }
922        Ok(Self {
923            distance_matrix,
924            variant: TSPVariant::Standard,
925            subtour_method: SubtourElimination::MillerTuckerZemlin,
926        })
927    }
928    /// Set variant
929    pub fn with_variant(mut self, variant: TSPVariant) -> Self {
930        self.variant = variant;
931        self
932    }
933    /// Build QUBO formulation
934    pub fn build_qubo(&self) -> Result<(Array2<f64>, HashMap<String, usize>), String> {
935        let n = self.distance_matrix.shape()[0];
936        match &self.variant {
937            TSPVariant::Standard => self.build_standard_tsp_qubo(n),
938            TSPVariant::MTSP { num_salesmen } => self.build_mtsp_qubo(n, *num_salesmen),
939            _ => self.build_standard_tsp_qubo(n),
940        }
941    }
942    /// Build standard TSP QUBO
943    fn build_standard_tsp_qubo(
944        &self,
945        n: usize,
946    ) -> Result<(Array2<f64>, HashMap<String, usize>), String> {
947        let n_vars = n * n;
948        let mut qubo = Array2::zeros((n_vars, n_vars));
949        let mut var_map = HashMap::new();
950        for i in 0..n {
951            for t in 0..n {
952                let var_name = format!("x_{i}_{t}");
953                var_map.insert(var_name, i * n + t);
954            }
955        }
956        let penalty = 1000.0;
957        for t in 0..n - 1 {
958            for i in 0..n {
959                for j in 0..n {
960                    if i != j {
961                        let var1 = i * n + t;
962                        let var2 = j * n + (t + 1);
963                        qubo[[var1, var2]] += self.distance_matrix[[i, j]];
964                    }
965                }
966            }
967        }
968        for i in 0..n {
969            for t1 in 0..n {
970                let idx1 = i * n + t1;
971                qubo[[idx1, idx1]] -= 2.0 * penalty;
972                for t2 in 0..n {
973                    let idx2 = i * n + t2;
974                    qubo[[idx1, idx2]] += penalty;
975                }
976            }
977        }
978        for t in 0..n {
979            for i1 in 0..n {
980                let idx1 = i1 * n + t;
981                qubo[[idx1, idx1]] -= 2.0 * penalty;
982                for i2 in 0..n {
983                    let idx2 = i2 * n + t;
984                    qubo[[idx1, idx2]] += penalty;
985                }
986            }
987        }
988        Ok((qubo, var_map))
989    }
990    /// Build Multiple TSP QUBO
991    fn build_mtsp_qubo(
992        &self,
993        n: usize,
994        num_salesmen: usize,
995    ) -> Result<(Array2<f64>, HashMap<String, usize>), String> {
996        let n_vars = num_salesmen * n * n;
997        let qubo = Array2::zeros((n_vars, n_vars));
998        let mut var_map = HashMap::new();
999        for s in 0..num_salesmen {
1000            for i in 0..n {
1001                for t in 0..n {
1002                    let var_name = format!("x_{s}_{i}_{t}");
1003                    var_map.insert(var_name, s * n * n + i * n + t);
1004                }
1005            }
1006        }
1007        Ok((qubo, var_map))
1008    }
1009    /// Decode TSP solution
1010    pub fn decode_solution(&self, solution: &HashMap<String, bool>) -> Vec<usize> {
1011        let n = self.distance_matrix.shape()[0];
1012        let mut tour = vec![0; n];
1013        for i in 0..n {
1014            for t in 0..n {
1015                let var_name = format!("x_{i}_{t}");
1016                if *solution.get(&var_name).unwrap_or(&false) {
1017                    tour[t] = i;
1018                }
1019            }
1020        }
1021        tour
1022    }
1023}
1024#[derive(Debug, Clone)]
1025pub struct SupplyChainNetwork {
1026    /// Suppliers
1027    pub suppliers: Vec<Supplier>,
1028    /// Warehouses
1029    pub warehouses: Vec<Warehouse>,
1030    /// Distribution centers
1031    pub distribution_centers: Vec<DistributionCenter>,
1032    /// Customers
1033    pub customers: Vec<Customer>,
1034    /// Transportation links
1035    pub links: Vec<TransportLink>,
1036}
1037#[derive(Debug, Clone, PartialEq, Eq)]
1038pub enum NodeType {
1039    Supplier,
1040    Warehouse,
1041    DistributionCenter,
1042    Customer,
1043}
1044#[derive(Debug, Clone)]
1045pub enum PickingPolicy {
1046    /// Single order picking
1047    Single,
1048    /// Batch picking
1049    Batch { size: usize },
1050    /// Zone picking
1051    Zone { zones: Vec<Vec<usize>> },
1052    /// Wave picking
1053    Wave { interval: usize },
1054}
1055#[derive(Debug, Clone)]
1056pub enum SubtourElimination {
1057    /// MTZ constraints
1058    MillerTuckerZemlin,
1059    /// DFJ constraints
1060    DantzigFulkersonJohnson,
1061    /// Flow-based
1062    FlowBased,
1063    /// Lazy constraints
1064    Lazy,
1065}
1066#[derive(Debug, Clone)]
1067pub enum AssignmentPolicy {
1068    /// Random storage
1069    Random,
1070    /// ABC classification
1071    ABC {
1072        a_locations: Vec<usize>,
1073        b_locations: Vec<usize>,
1074        c_locations: Vec<usize>,
1075    },
1076    /// Dedicated storage
1077    Dedicated,
1078    /// Class-based storage
1079    ClassBased,
1080}
1081#[derive(Debug, Clone)]
1082pub enum SupplyChainObjective {
1083    /// Minimize total cost
1084    MinimizeCost,
1085    /// Minimize delivery time
1086    MinimizeDeliveryTime,
1087    /// Maximize service level
1088    MaximizeServiceLevel,
1089    /// Minimize inventory
1090    MinimizeInventory,
1091    /// Balance workload
1092    BalanceWorkload,
1093}
1094/// Vehicle Routing Problem for optimization
1095pub struct VehicleRoutingProblem {
1096    pub optimizer: VehicleRoutingOptimizer,
1097}
1098impl VehicleRoutingProblem {
1099    pub const fn new(optimizer: VehicleRoutingOptimizer) -> Self {
1100        Self { optimizer }
1101    }
1102    /// Evaluate floating point solution
1103    pub fn evaluate_continuous(&self, x: &Array1<f64>) -> f64 {
1104        let n_locations = self.optimizer.distance_matrix.shape()[0];
1105        let n_vars = self.optimizer.num_vehicles * n_locations * n_locations;
1106        if x.len() != n_vars {
1107            return f64::INFINITY;
1108        }
1109        let mut energy = 0.0;
1110        let mut var_idx = 0;
1111        for _v in 0..self.optimizer.num_vehicles {
1112            for i in 0..n_locations {
1113                for j in 0..n_locations {
1114                    if i != j {
1115                        let decision = if x[var_idx] > 0.5 { 1.0 } else { 0.0 };
1116                        energy += decision * self.optimizer.distance_matrix[[i, j]];
1117                    }
1118                    var_idx += 1;
1119                }
1120            }
1121        }
1122        energy += self.calculate_constraint_penalties(x);
1123        energy
1124    }
1125    fn calculate_constraint_penalties(&self, x: &Array1<f64>) -> f64 {
1126        let penalty = 1000.0;
1127        let mut total_penalty = 0.0;
1128        let n_locations = self.optimizer.distance_matrix.shape()[0];
1129        for j in 1..n_locations {
1130            let mut visits = 0.0;
1131            let mut var_idx = 0;
1132            for _v in 0..self.optimizer.num_vehicles {
1133                for i in 0..n_locations {
1134                    if i != j {
1135                        let decision = if x[var_idx + i * n_locations + j] > 0.5 {
1136                            1.0
1137                        } else {
1138                            0.0
1139                        };
1140                        visits += decision;
1141                    }
1142                }
1143                var_idx += n_locations * n_locations;
1144            }
1145            total_penalty += penalty * (visits - 1.0f64).abs();
1146        }
1147        total_penalty
1148    }
1149}
1150#[derive(Debug, Clone)]
1151pub struct TimeWindow {
1152    /// Earliest arrival time
1153    pub start: f64,
1154    /// Latest arrival time
1155    pub end: f64,
1156    /// Service time at location
1157    pub service_time: f64,
1158}
1159/// Binary Vehicle Routing Problem wrapper
1160pub struct BinaryVehicleRoutingProblem {
1161    inner: VehicleRoutingProblem,
1162}
1163impl BinaryVehicleRoutingProblem {
1164    pub const fn new(optimizer: VehicleRoutingOptimizer) -> Self {
1165        Self {
1166            inner: VehicleRoutingProblem::new(optimizer),
1167        }
1168    }
1169    /// Get the number of variables needed for binary representation
1170    pub fn num_variables(&self) -> usize {
1171        let n_locations = self.inner.optimizer.distance_matrix.shape()[0];
1172        self.inner.optimizer.num_vehicles * n_locations * n_locations
1173    }
1174    /// Evaluate binary solution directly
1175    pub fn evaluate_binary(&self, solution: &[i8]) -> f64 {
1176        let x: Array1<f64> = solution.iter().map(|&b| b as f64).collect();
1177        self.inner.evaluate_continuous(&x)
1178    }
1179    /// Create random binary solution
1180    pub fn random_solution(&self) -> Vec<i8> {
1181        let mut rng = thread_rng();
1182        let n_vars = self.num_variables();
1183        (0..n_vars)
1184            .map(|_| i8::from(rng.random::<f64>() > 0.8))
1185            .collect()
1186    }
1187    /// Convert binary solution to routes
1188    pub fn decode_binary_solution(&self, solution: &[i8]) -> Vec<Route> {
1189        let mut bool_solution = HashMap::new();
1190        let n_locations = self.inner.optimizer.distance_matrix.shape()[0];
1191        let mut var_idx = 0;
1192        for v in 0..self.inner.optimizer.num_vehicles {
1193            for i in 0..n_locations {
1194                for j in 0..n_locations {
1195                    let var_name = format!("x_{v}_{i}_{j}");
1196                    bool_solution.insert(var_name, solution[var_idx] == 1);
1197                    var_idx += 1;
1198                }
1199            }
1200        }
1201        self.inner.optimizer.decode_solution(&bool_solution)
1202    }
1203}