solverforge_solver/heuristic/selector/
sublist_swap.rs1use std::fmt::Debug;
65use std::marker::PhantomData;
66
67use solverforge_core::domain::PlanningSolution;
68use solverforge_scoring::Director;
69
70use crate::heuristic::r#move::{ListMoveImpl, SubListSwapMove};
71
72use super::entity::EntitySelector;
73use super::typed_move_selector::MoveSelector;
74
75pub struct SubListSwapMoveSelector<S, V, ES> {
85 entity_selector: ES,
86 min_sublist_size: usize,
88 max_sublist_size: usize,
90 list_len: fn(&S, usize) -> usize,
91 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
92 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
93 variable_name: &'static str,
94 descriptor_index: usize,
95 _phantom: PhantomData<(fn() -> S, fn() -> V)>,
96}
97
98impl<S, V: Debug, ES: Debug> Debug for SubListSwapMoveSelector<S, V, ES> {
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 f.debug_struct("SubListSwapMoveSelector")
101 .field("entity_selector", &self.entity_selector)
102 .field("min_sublist_size", &self.min_sublist_size)
103 .field("max_sublist_size", &self.max_sublist_size)
104 .field("variable_name", &self.variable_name)
105 .field("descriptor_index", &self.descriptor_index)
106 .finish()
107 }
108}
109
110impl<S, V, ES> SubListSwapMoveSelector<S, V, ES> {
111 #[allow(clippy::too_many_arguments)]
127 pub fn new(
128 entity_selector: ES,
129 min_sublist_size: usize,
130 max_sublist_size: usize,
131 list_len: fn(&S, usize) -> usize,
132 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
133 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
134 variable_name: &'static str,
135 descriptor_index: usize,
136 ) -> Self {
137 assert!(min_sublist_size >= 1, "min_sublist_size must be at least 1");
138 assert!(
139 max_sublist_size >= min_sublist_size,
140 "max_sublist_size must be >= min_sublist_size"
141 );
142 Self {
143 entity_selector,
144 min_sublist_size,
145 max_sublist_size,
146 list_len,
147 sublist_remove,
148 sublist_insert,
149 variable_name,
150 descriptor_index,
151 _phantom: PhantomData,
152 }
153 }
154}
155
156impl<S, V, ES> MoveSelector<S, SubListSwapMove<S, V>> for SubListSwapMoveSelector<S, V, ES>
157where
158 S: PlanningSolution,
159 V: Clone + PartialEq + Send + Sync + Debug + 'static,
160 ES: EntitySelector<S>,
161{
162 fn iter_moves<'a, D: Director<S>>(
163 &'a self,
164 score_director: &'a D,
165 ) -> impl Iterator<Item = SubListSwapMove<S, V>> + 'a {
166 let solution = score_director.working_solution();
167 let list_len = self.list_len;
168 let sublist_remove = self.sublist_remove;
169 let sublist_insert = self.sublist_insert;
170 let variable_name = self.variable_name;
171 let descriptor_index = self.descriptor_index;
172 let min_seg = self.min_sublist_size;
173 let max_seg = self.max_sublist_size;
174
175 let entities: Vec<usize> = self
176 .entity_selector
177 .iter(score_director)
178 .map(|r| r.entity_index)
179 .collect();
180
181 let route_lens: Vec<usize> = entities.iter().map(|&e| list_len(solution, e)).collect();
182
183 let mut moves = Vec::new();
184
185 for (i, &entity_a) in entities.iter().enumerate() {
186 let len_a = route_lens[i];
187
188 for first_start in 0..len_a {
191 for first_size in min_seg..=max_seg {
192 let first_end = first_start + first_size;
193 if first_end > len_a {
194 break;
195 }
196
197 for second_start in first_end..len_a {
199 for second_size in min_seg..=max_seg {
200 let second_end = second_start + second_size;
201 if second_end > len_a {
202 break;
203 }
204 moves.push(SubListSwapMove::new(
205 entity_a,
206 first_start,
207 first_end,
208 entity_a,
209 second_start,
210 second_end,
211 list_len,
212 sublist_remove,
213 sublist_insert,
214 variable_name,
215 descriptor_index,
216 ));
217 }
218 }
219 }
220 }
221
222 for (j, &entity_b) in entities.iter().enumerate() {
224 if j <= i {
225 continue;
226 }
227 let len_b = route_lens[j];
228 if len_b == 0 {
229 continue;
230 }
231
232 for first_start in 0..len_a {
233 for first_size in min_seg..=max_seg {
234 let first_end = first_start + first_size;
235 if first_end > len_a {
236 break;
237 }
238
239 for second_start in 0..len_b {
240 for second_size in min_seg..=max_seg {
241 let second_end = second_start + second_size;
242 if second_end > len_b {
243 break;
244 }
245 moves.push(SubListSwapMove::new(
246 entity_a,
247 first_start,
248 first_end,
249 entity_b,
250 second_start,
251 second_end,
252 list_len,
253 sublist_remove,
254 sublist_insert,
255 variable_name,
256 descriptor_index,
257 ));
258 }
259 }
260 }
261 }
262 }
263 }
264
265 moves.into_iter()
266 }
267
268 fn size<D: Director<S>>(&self, score_director: &D) -> usize {
269 let solution = score_director.working_solution();
270 let list_len = self.list_len;
271
272 let entities: Vec<usize> = self
273 .entity_selector
274 .iter(score_director)
275 .map(|r| r.entity_index)
276 .collect();
277
278 let route_lens: Vec<usize> = entities.iter().map(|&e| list_len(solution, e)).collect();
279 let n = entities.len();
280 if n == 0 {
281 return 0;
282 }
283
284 let k_range = self.max_sublist_size - self.min_sublist_size + 1;
285 let total_elements: usize = route_lens.iter().sum();
286 let avg_len = total_elements / n;
287 n * avg_len * k_range * avg_len.max(1) * k_range * (n + 1) / 2
289 }
290}
291
292pub struct ListMoveSubListSwapSelector<S, V, ES> {
294 inner: SubListSwapMoveSelector<S, V, ES>,
295}
296
297impl<S, V: Debug, ES: Debug> Debug for ListMoveSubListSwapSelector<S, V, ES> {
298 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
299 f.debug_struct("ListMoveSubListSwapSelector")
300 .field("inner", &self.inner)
301 .finish()
302 }
303}
304
305impl<S, V, ES> ListMoveSubListSwapSelector<S, V, ES> {
306 pub fn new(inner: SubListSwapMoveSelector<S, V, ES>) -> Self {
308 Self { inner }
309 }
310}
311
312impl<S, V, ES> MoveSelector<S, ListMoveImpl<S, V>> for ListMoveSubListSwapSelector<S, V, ES>
313where
314 S: PlanningSolution,
315 V: Clone + PartialEq + Send + Sync + Debug + 'static,
316 ES: EntitySelector<S>,
317{
318 fn iter_moves<'a, D: Director<S>>(
319 &'a self,
320 score_director: &'a D,
321 ) -> impl Iterator<Item = ListMoveImpl<S, V>> + 'a {
322 self.inner
323 .iter_moves(score_director)
324 .map(ListMoveImpl::SubListSwap)
325 }
326
327 fn size<D: Director<S>>(&self, score_director: &D) -> usize {
328 self.inner.size(score_director)
329 }
330}