1use std::fmt::Debug;
4use std::marker::PhantomData;
5
6use solverforge_core::domain::PlanningSolution;
7use solverforge_scoring::Director;
8
9use crate::heuristic::r#move::k_opt_reconnection::{
10 enumerate_reconnections, KOptReconnection, THREE_OPT_RECONNECTIONS,
11};
12use crate::heuristic::r#move::{CutPoint, KOptMove};
13
14use super::super::entity::EntitySelector;
15use super::super::move_selector::MoveSelector;
16use super::config::KOptConfig;
17use super::distance_meter::ListPositionDistanceMeter;
18
19pub struct NearbyKOptMoveSelector<S, V, D: ListPositionDistanceMeter<S>, ES> {
33 entity_selector: ES,
35 distance_meter: D,
37 max_nearby: usize,
39 config: KOptConfig,
41 patterns: Vec<&'static KOptReconnection>,
43 list_len: fn(&S, usize) -> usize,
44 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
46 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
48 variable_name: &'static str,
50 descriptor_index: usize,
52 _phantom: PhantomData<(fn() -> S, fn() -> V)>,
53}
54
55impl<S, V: Debug, D: ListPositionDistanceMeter<S>, ES: Debug> Debug
56 for NearbyKOptMoveSelector<S, V, D, ES>
57{
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 f.debug_struct("NearbyKOptMoveSelector")
60 .field("entity_selector", &self.entity_selector)
61 .field("max_nearby", &self.max_nearby)
62 .field("config", &self.config)
63 .field("pattern_count", &self.patterns.len())
64 .finish()
65 }
66}
67
68impl<S: PlanningSolution, V, D: ListPositionDistanceMeter<S>, ES>
69 NearbyKOptMoveSelector<S, V, D, ES>
70{
71 #[allow(clippy::too_many_arguments)]
72 pub fn new(
73 entity_selector: ES,
74 distance_meter: D,
75 max_nearby: usize,
76 config: KOptConfig,
77 list_len: fn(&S, usize) -> usize,
78 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
79 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
80 variable_name: &'static str,
81 descriptor_index: usize,
82 ) -> Self {
83 let patterns: Vec<&'static KOptReconnection> = if config.k == 3 {
84 THREE_OPT_RECONNECTIONS.iter().collect()
85 } else {
86 let generated = enumerate_reconnections(config.k);
87 let leaked: &'static [KOptReconnection] = Box::leak(generated.into_boxed_slice());
88 leaked.iter().collect()
89 };
90
91 Self {
92 entity_selector,
93 distance_meter,
94 max_nearby,
95 config,
96 patterns,
97 list_len,
98 sublist_remove,
99 sublist_insert,
100 variable_name,
101 descriptor_index,
102 _phantom: PhantomData,
103 }
104 }
105
106 fn nearby_positions(
107 &self,
108 solution: &S,
109 entity_idx: usize,
110 origin: usize,
111 len: usize,
112 ) -> Vec<usize> {
113 let mut positions: Vec<(usize, f64)> = (0..len)
114 .filter(|&p| p != origin)
115 .map(|p| {
116 let dist = self
117 .distance_meter
118 .distance(solution, entity_idx, origin, p);
119 (p, dist)
120 })
121 .collect();
122
123 positions.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
124 positions.truncate(self.max_nearby);
125 positions.into_iter().map(|(p, _)| p).collect()
126 }
127}
128
129impl<S, V, DM, ES> MoveSelector<S, KOptMove<S, V>> for NearbyKOptMoveSelector<S, V, DM, ES>
130where
131 S: PlanningSolution,
132 V: Clone + Send + Sync + Debug + 'static,
133 DM: ListPositionDistanceMeter<S> + 'static,
134 ES: EntitySelector<S>,
135{
136 fn open_cursor<'a, SD: Director<S>>(
137 &'a self,
138 score_director: &SD,
139 ) -> impl Iterator<Item = KOptMove<S, V>> + 'a {
140 let k = self.config.k;
141 let min_seg = self.config.min_segment_len;
142 let patterns = &self.patterns;
143 let list_len_fn = self.list_len;
144 let sublist_remove = self.sublist_remove;
145 let sublist_insert = self.sublist_insert;
146 let variable_name = self.variable_name;
147 let descriptor_index = self.descriptor_index;
148 let solution = score_director.working_solution();
149 let moves: Vec<_> = self
150 .entity_selector
151 .iter(score_director)
152 .flat_map(move |entity_ref| {
153 let entity_idx = entity_ref.entity_index;
154 let len = list_len_fn(solution, entity_idx);
155 let cuts_iter = NearbyCutIterator::new(self, solution, entity_idx, k, len, min_seg);
156
157 cuts_iter.flat_map(move |cuts| {
158 patterns.iter().map(move |&pattern| {
159 let mut sorted_cuts = cuts.clone();
160 sorted_cuts.sort_by_key(|c| c.position());
161
162 KOptMove::new(
163 &sorted_cuts,
164 pattern,
165 list_len_fn,
166 sublist_remove,
167 sublist_insert,
168 variable_name,
169 descriptor_index,
170 )
171 })
172 })
173 })
174 .collect();
175 moves.into_iter()
176 }
177
178 fn size<SD: Director<S>>(&self, score_director: &SD) -> usize {
179 let k = self.config.k;
181 let m = self.max_nearby;
182 let pattern_count = self.patterns.len();
183
184 self.entity_selector
185 .iter(score_director)
186 .map(|entity_ref| {
187 let solution = score_director.working_solution();
188 let len = (self.list_len)(solution, entity_ref.entity_index);
189 if len < (k + 1) * self.config.min_segment_len {
190 0
191 } else {
192 len.saturating_sub(k) * m.pow((k - 1) as u32) * pattern_count
194 }
195 })
196 .sum()
197 }
198}
199
200struct NearbyCutIterator<'a, S, V, D: ListPositionDistanceMeter<S>, ES> {
202 selector: &'a NearbyKOptMoveSelector<S, V, D, ES>,
203 solution: &'a S,
204 entity_idx: usize,
205 k: usize,
206 len: usize,
207 min_seg: usize,
208 stack: Vec<(usize, usize)>,
210 nearby_cache: Vec<Vec<usize>>,
212 done: bool,
213}
214
215impl<'a, S: PlanningSolution, V, D: ListPositionDistanceMeter<S>, ES>
216 NearbyCutIterator<'a, S, V, D, ES>
217{
218 fn new(
219 selector: &'a NearbyKOptMoveSelector<S, V, D, ES>,
220 solution: &'a S,
221 entity_idx: usize,
222 k: usize,
223 len: usize,
224 min_seg: usize,
225 ) -> Self {
226 let min_len = (k + 1) * min_seg;
227 if len < min_len {
228 return Self {
229 selector,
230 solution,
231 entity_idx,
232 k,
233 len,
234 min_seg,
235 stack: vec![],
236 nearby_cache: vec![],
237 done: true,
238 };
239 }
240
241 let mut iter = Self {
243 selector,
244 solution,
245 entity_idx,
246 k,
247 len,
248 min_seg,
249 stack: vec![(min_seg, 0)],
250 nearby_cache: vec![vec![]],
251 done: false,
252 };
253
254 iter.extend_stack();
256 iter
257 }
258
259 fn extend_stack(&mut self) {
260 while self.stack.len() < self.k && !self.done {
261 let (last_pos, _) = *self.stack.last().unwrap();
262
263 let nearby =
265 self.selector
266 .nearby_positions(self.solution, self.entity_idx, last_pos, self.len);
267
268 let remaining_cuts = self.k - self.stack.len();
270 let min_pos = last_pos + self.min_seg;
271 let max_pos = self.len - self.min_seg * remaining_cuts;
272
273 let valid: Vec<usize> = nearby
274 .into_iter()
275 .filter(|&p| p >= min_pos && p <= max_pos)
276 .collect();
277
278 if valid.is_empty() {
279 if !self.backtrack() {
281 self.done = true;
282 return;
283 }
284 } else {
285 self.nearby_cache.push(valid);
286 let next_pos = self.nearby_cache.last().unwrap()[0];
287 self.stack.push((next_pos, 0));
288 }
289 }
290 }
291
292 fn backtrack(&mut self) -> bool {
293 while let Some((popped_pos, _idx)) = self.stack.pop() {
294 self.nearby_cache.pop();
295
296 if let Some((_, last_idx)) = self.stack.last_mut() {
297 let cache_idx = self.nearby_cache.len();
298 if cache_idx > 0 {
299 let cache = &self.nearby_cache[cache_idx - 1];
300 let next_idx = *last_idx + 1;
301 if next_idx < cache.len() {
302 *last_idx = next_idx;
303 let (pos, _) = self.stack.last().unwrap();
304 let new_pos = cache[next_idx];
305 if new_pos > *pos {
306 self.stack.pop();
307 self.stack.push((new_pos, next_idx));
308 return true;
309 }
310 }
311 }
312 } else {
313 let next_first = popped_pos + 1;
315 let max_first = self.len - self.min_seg * self.k;
316 if next_first <= max_first {
317 self.stack.push((next_first, 0));
318 self.nearby_cache.push(vec![]);
319 return true;
320 }
321 }
322 }
323 false
324 }
325
326 fn advance(&mut self) {
327 if self.done || self.stack.is_empty() {
328 self.done = true;
329 return;
330 }
331
332 if let Some((_, idx)) = self.stack.last_mut() {
334 let cache_idx = self.nearby_cache.len() - 1;
335 let cache = &self.nearby_cache[cache_idx];
336 let next_idx = *idx + 1;
337 if next_idx < cache.len() {
338 *idx = next_idx;
339 let new_pos = cache[next_idx];
340 self.stack.pop();
341 self.stack.push((new_pos, next_idx));
342 return;
343 }
344 }
345
346 if self.backtrack() {
348 self.extend_stack();
349 } else {
350 self.done = true;
351 }
352 }
353}
354
355impl<'a, S: PlanningSolution, V, D: ListPositionDistanceMeter<S>, ES> Iterator
356 for NearbyCutIterator<'a, S, V, D, ES>
357{
358 type Item = Vec<CutPoint>;
359
360 fn next(&mut self) -> Option<Self::Item> {
361 if self.done || self.stack.len() != self.k {
362 return None;
363 }
364
365 let cuts: Vec<CutPoint> = self
366 .stack
367 .iter()
368 .map(|(pos, _)| CutPoint::new(self.entity_idx, *pos))
369 .collect();
370
371 self.advance();
372
373 Some(cuts)
374 }
375}