Skip to main content

solverforge_solver/builder/context/
list.rs

1use std::fmt;
2use std::marker::PhantomData;
3
4use crate::heuristic::selector::k_opt::ListPositionDistanceMeter;
5use crate::heuristic::selector::nearby_list_change::CrossEntityDistanceMeter;
6
7pub struct IntraDistanceAdapter<T>(pub T);
8
9/// Canonical source key for the built-in `usize` planning-list representation.
10/// Generated list models and dynamic bridge lists use their logical element ID
11/// as the stable key; the per-run runtime binder maps it to source order.
12pub fn usize_element_source_key<S>(_solution: &S, element: &usize) -> usize {
13    *element
14}
15
16impl<S, T: CrossEntityDistanceMeter<S>> ListPositionDistanceMeter<S> for IntraDistanceAdapter<T> {
17    fn distance(&self, solution: &S, entity_idx: usize, pos_a: usize, pos_b: usize) -> f64 {
18        self.0
19            .distance(solution, entity_idx, pos_a, entity_idx, pos_b)
20    }
21}
22
23pub struct ListVariableSlot<S, V, DM, IDM> {
24    pub entity_type_name: &'static str,
25    pub element_count: fn(&S) -> usize,
26    pub assigned_elements: fn(&S) -> Vec<V>,
27    pub list_len: fn(&S, usize) -> usize,
28    pub list_remove: fn(&mut S, usize, usize) -> Option<V>,
29    pub construction_list_remove: fn(&mut S, usize, usize) -> V,
30    pub list_insert: fn(&mut S, usize, usize, V),
31    pub list_get: fn(&S, usize, usize) -> Option<V>,
32    pub list_set: fn(&mut S, usize, usize, V),
33    pub list_reverse: fn(&mut S, usize, usize, usize),
34    pub sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
35    pub sublist_insert: fn(&mut S, usize, usize, Vec<V>),
36    pub ruin_remove: fn(&mut S, usize, usize) -> V,
37    pub ruin_insert: fn(&mut S, usize, usize, V),
38    pub index_to_element: fn(&S, usize) -> V,
39    /// Stable identity key for a list element. The per-run runtime binder
40    /// resolves these keys to declared source indexes once; it never scans
41    /// payloads by `PartialEq` during construction.
42    pub element_source_key: fn(&S, &V) -> usize,
43    pub entity_count: fn(&S) -> usize,
44    pub cross_distance_meter: DM,
45    pub intra_distance_meter: IDM,
46    pub variable_name: &'static str,
47    pub descriptor_index: usize,
48    pub route_get_fn: Option<fn(&S, usize) -> Vec<usize>>,
49    pub route_set_fn: Option<fn(&mut S, usize, Vec<usize>)>,
50    pub route_depot_fn: Option<fn(&S, usize) -> usize>,
51    pub route_distance_fn: Option<fn(&S, usize, usize, usize) -> i64>,
52    pub route_feasible_fn: Option<fn(&S, usize, &[usize]) -> bool>,
53    pub savings_depot_fn: Option<fn(&S, usize) -> usize>,
54    pub savings_metric_class_fn: Option<fn(&S, usize) -> usize>,
55    pub savings_distance_fn: Option<fn(&S, usize, usize, usize) -> i64>,
56    pub savings_feasible_fn: Option<fn(&S, usize, &[usize]) -> bool>,
57    pub element_owner_fn: Option<fn(&S, &V) -> Option<usize>>,
58    pub construction_element_order_key: Option<fn(&S, V) -> i64>,
59    pub precedence_duration_fn: Option<fn(&S, V) -> usize>,
60    pub precedence_successors_fn: Option<fn(&S, V, &mut Vec<V>)>,
61    _phantom: PhantomData<(fn() -> S, fn() -> V)>,
62}
63
64impl<S, V, DM: Clone, IDM: Clone> Clone for ListVariableSlot<S, V, DM, IDM> {
65    fn clone(&self) -> Self {
66        Self {
67            entity_type_name: self.entity_type_name,
68            element_count: self.element_count,
69            assigned_elements: self.assigned_elements,
70            list_len: self.list_len,
71            list_remove: self.list_remove,
72            construction_list_remove: self.construction_list_remove,
73            list_insert: self.list_insert,
74            list_get: self.list_get,
75            list_set: self.list_set,
76            list_reverse: self.list_reverse,
77            sublist_remove: self.sublist_remove,
78            sublist_insert: self.sublist_insert,
79            ruin_remove: self.ruin_remove,
80            ruin_insert: self.ruin_insert,
81            index_to_element: self.index_to_element,
82            element_source_key: self.element_source_key,
83            entity_count: self.entity_count,
84            cross_distance_meter: self.cross_distance_meter.clone(),
85            intra_distance_meter: self.intra_distance_meter.clone(),
86            variable_name: self.variable_name,
87            descriptor_index: self.descriptor_index,
88            route_get_fn: self.route_get_fn,
89            route_set_fn: self.route_set_fn,
90            route_depot_fn: self.route_depot_fn,
91            route_distance_fn: self.route_distance_fn,
92            route_feasible_fn: self.route_feasible_fn,
93            savings_depot_fn: self.savings_depot_fn,
94            savings_metric_class_fn: self.savings_metric_class_fn,
95            savings_distance_fn: self.savings_distance_fn,
96            savings_feasible_fn: self.savings_feasible_fn,
97            element_owner_fn: self.element_owner_fn,
98            construction_element_order_key: self.construction_element_order_key,
99            precedence_duration_fn: self.precedence_duration_fn,
100            precedence_successors_fn: self.precedence_successors_fn,
101            _phantom: PhantomData,
102        }
103    }
104}
105
106impl<S, V, DM, IDM> ListVariableSlot<S, V, DM, IDM> {
107    #[allow(clippy::too_many_arguments)]
108    pub fn new(
109        entity_type_name: &'static str,
110        element_count: fn(&S) -> usize,
111        assigned_elements: fn(&S) -> Vec<V>,
112        list_len: fn(&S, usize) -> usize,
113        list_remove: fn(&mut S, usize, usize) -> Option<V>,
114        construction_list_remove: fn(&mut S, usize, usize) -> V,
115        list_insert: fn(&mut S, usize, usize, V),
116        list_get: fn(&S, usize, usize) -> Option<V>,
117        list_set: fn(&mut S, usize, usize, V),
118        list_reverse: fn(&mut S, usize, usize, usize),
119        sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
120        sublist_insert: fn(&mut S, usize, usize, Vec<V>),
121        ruin_remove: fn(&mut S, usize, usize) -> V,
122        ruin_insert: fn(&mut S, usize, usize, V),
123        index_to_element: fn(&S, usize) -> V,
124        element_source_key: fn(&S, &V) -> usize,
125        entity_count: fn(&S) -> usize,
126        cross_distance_meter: DM,
127        intra_distance_meter: IDM,
128        variable_name: &'static str,
129        descriptor_index: usize,
130        route_get_fn: Option<fn(&S, usize) -> Vec<usize>>,
131        route_set_fn: Option<fn(&mut S, usize, Vec<usize>)>,
132        route_depot_fn: Option<fn(&S, usize) -> usize>,
133        route_distance_fn: Option<fn(&S, usize, usize, usize) -> i64>,
134        route_feasible_fn: Option<fn(&S, usize, &[usize]) -> bool>,
135        savings_depot_fn: Option<fn(&S, usize) -> usize>,
136        savings_metric_class_fn: Option<fn(&S, usize) -> usize>,
137        savings_distance_fn: Option<fn(&S, usize, usize, usize) -> i64>,
138        savings_feasible_fn: Option<fn(&S, usize, &[usize]) -> bool>,
139    ) -> Self {
140        Self {
141            entity_type_name,
142            element_count,
143            assigned_elements,
144            list_len,
145            list_remove,
146            construction_list_remove,
147            list_insert,
148            list_get,
149            list_set,
150            list_reverse,
151            sublist_remove,
152            sublist_insert,
153            ruin_remove,
154            ruin_insert,
155            index_to_element,
156            element_source_key,
157            entity_count,
158            cross_distance_meter,
159            intra_distance_meter,
160            variable_name,
161            descriptor_index,
162            route_get_fn,
163            route_set_fn,
164            route_depot_fn,
165            route_distance_fn,
166            route_feasible_fn,
167            savings_depot_fn,
168            savings_metric_class_fn,
169            savings_distance_fn,
170            savings_feasible_fn,
171            element_owner_fn: None,
172            construction_element_order_key: None,
173            precedence_duration_fn: None,
174            precedence_successors_fn: None,
175            _phantom: PhantomData,
176        }
177    }
178
179    pub fn with_element_owner_fn(
180        mut self,
181        element_owner_fn: Option<fn(&S, &V) -> Option<usize>>,
182    ) -> Self {
183        self.element_owner_fn = element_owner_fn;
184        self
185    }
186
187    pub fn with_construction_element_order_key(
188        mut self,
189        construction_element_order_key: Option<fn(&S, V) -> i64>,
190    ) -> Self {
191        self.construction_element_order_key = construction_element_order_key;
192        self
193    }
194
195    pub fn with_precedence_hooks(
196        mut self,
197        duration_fn: Option<fn(&S, V) -> usize>,
198        successors_fn: Option<fn(&S, V, &mut Vec<V>)>,
199    ) -> Self {
200        self.precedence_duration_fn = duration_fn;
201        self.precedence_successors_fn = successors_fn;
202        self
203    }
204
205    pub fn matches_target(&self, entity_class: Option<&str>, variable_name: Option<&str>) -> bool {
206        entity_class.is_none_or(|name| name == self.entity_type_name)
207            && variable_name.is_none_or(|name| name == self.variable_name)
208    }
209
210    pub fn has_unassigned_elements(&self, solution: &S) -> bool {
211        (self.assigned_elements)(solution).len() < (self.element_count)(solution)
212    }
213
214    pub fn has_list_content(&self, solution: &S) -> bool {
215        (0..(self.entity_count)(solution))
216            .any(|entity_index| (self.list_len)(solution, entity_index) > 0)
217    }
218
219    pub fn supports_clarke_wright(&self) -> bool {
220        self.route_set_fn.is_some()
221            && self.savings_depot_fn.is_some()
222            && self.savings_distance_fn.is_some()
223            && self.savings_feasible_fn.is_some()
224    }
225
226    pub fn supports_k_opt(&self) -> bool {
227        self.route_get_fn.is_some()
228            && self.route_set_fn.is_some()
229            && self.route_depot_fn.is_some()
230            && self.route_distance_fn.is_some()
231    }
232
233    pub fn supports_ruin(&self) -> bool {
234        true
235    }
236
237    pub fn supports_precedence_moves(&self) -> bool {
238        self.precedence_duration_fn.is_some() && self.precedence_successors_fn.is_some()
239    }
240}
241
242impl<S, V, DM: fmt::Debug, IDM: fmt::Debug> fmt::Debug for ListVariableSlot<S, V, DM, IDM> {
243    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244        f.debug_struct("ListVariableSlot")
245            .field("entity_type_name", &self.entity_type_name)
246            .field("variable_name", &self.variable_name)
247            .field("descriptor_index", &self.descriptor_index)
248            .finish()
249    }
250}