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