Skip to main content

solverforge_solver/descriptor/selectors/
change_swap.rs

1struct DescriptorEntityValues {
2    entity_index: usize,
3    values: Vec<usize>,
4    current_assigned: bool,
5}
6
7pub struct DescriptorChangeMoveCursor<S>
8where
9    S: PlanningSolution,
10{
11    store: CandidateStore<S, DescriptorMoveUnion<S>>,
12    binding: VariableBinding,
13    solution_descriptor: SolutionDescriptor,
14    entity_values: Vec<DescriptorEntityValues>,
15    entity_offset: usize,
16    value_offset: usize,
17}
18
19impl<S> DescriptorChangeMoveCursor<S>
20where
21    S: PlanningSolution,
22{
23    fn new(
24        binding: VariableBinding,
25        solution_descriptor: SolutionDescriptor,
26        entity_values: Vec<DescriptorEntityValues>,
27    ) -> Self {
28        Self {
29            store: CandidateStore::new(),
30            binding,
31            solution_descriptor,
32            entity_values,
33            entity_offset: 0,
34            value_offset: 0,
35        }
36    }
37}
38
39impl<S> MoveCursor<S, DescriptorMoveUnion<S>> for DescriptorChangeMoveCursor<S>
40where
41    S: PlanningSolution + 'static,
42{
43    fn next_candidate(&mut self) -> Option<CandidateId> {
44        while let Some(entity_values) = self.entity_values.get(self.entity_offset) {
45            if let Some(value) = entity_values.values.get(self.value_offset).copied() {
46                self.value_offset += 1;
47                return Some(self.store.push(DescriptorMoveUnion::Change(
48                    DescriptorChangeMove::new(
49                        self.binding.clone(),
50                        entity_values.entity_index,
51                        Some(value),
52                        self.solution_descriptor.clone(),
53                    ),
54                )));
55            }
56
57            if self.binding.allows_unassigned
58                && entity_values.current_assigned
59                && self.value_offset == entity_values.values.len()
60            {
61                self.value_offset += 1;
62                return Some(self.store.push(DescriptorMoveUnion::Change(
63                    DescriptorChangeMove::new(
64                        self.binding.clone(),
65                        entity_values.entity_index,
66                        None,
67                        self.solution_descriptor.clone(),
68                    ),
69                )));
70            }
71
72            self.entity_offset += 1;
73            self.value_offset = 0;
74        }
75        None
76    }
77
78    fn candidate(&self, id: CandidateId) -> Option<MoveCandidateRef<'_, S, DescriptorMoveUnion<S>>> {
79        self.store.candidate(id)
80    }
81
82    fn take_candidate(&mut self, id: CandidateId) -> DescriptorMoveUnion<S> {
83        self.store.take_candidate(id)
84    }
85
86    fn release_candidate(&mut self, id: CandidateId) -> bool {
87        self.store.release_candidate(id)
88    }
89}
90
91impl<S> Iterator for DescriptorChangeMoveCursor<S>
92where
93    S: PlanningSolution + 'static,
94{
95    type Item = DescriptorMoveUnion<S>;
96
97    fn next(&mut self) -> Option<Self::Item> {
98        self.next_owned_candidate()
99    }
100}
101
102enum DescriptorSwapOrder {
103    All {
104        count: usize,
105        left_entity_index: usize,
106        right_entity_index: usize,
107    },
108    Explicit(std::vec::IntoIter<(usize, usize)>),
109}
110
111pub struct DescriptorSwapMoveCursor<S>
112where
113    S: PlanningSolution,
114{
115    store: CandidateStore<S, DescriptorMoveUnion<S>>,
116    binding: VariableBinding,
117    solution_descriptor: SolutionDescriptor,
118    legality_index: SwapLegalityIndex,
119    order: DescriptorSwapOrder,
120}
121
122impl<S> DescriptorSwapMoveCursor<S>
123where
124    S: PlanningSolution,
125{
126    fn all(
127        binding: VariableBinding,
128        solution_descriptor: SolutionDescriptor,
129        legality_index: SwapLegalityIndex,
130        count: usize,
131    ) -> Self {
132        Self {
133            store: CandidateStore::new(),
134            binding,
135            solution_descriptor,
136            legality_index,
137            order: DescriptorSwapOrder::All {
138                count,
139                left_entity_index: 0,
140                right_entity_index: 1,
141            },
142        }
143    }
144
145    fn explicit(
146        binding: VariableBinding,
147        solution_descriptor: SolutionDescriptor,
148        legality_index: SwapLegalityIndex,
149        pairs: Vec<(usize, usize)>,
150    ) -> Self {
151        Self {
152            store: CandidateStore::new(),
153            binding,
154            solution_descriptor,
155            legality_index,
156            order: DescriptorSwapOrder::Explicit(pairs.into_iter()),
157        }
158    }
159
160    fn next_pair(&mut self) -> Option<(usize, usize)> {
161        match &mut self.order {
162            DescriptorSwapOrder::All {
163                count,
164                left_entity_index,
165                right_entity_index,
166            } => {
167                while *left_entity_index < *count {
168                    if *right_entity_index < *count {
169                        let pair = (*left_entity_index, *right_entity_index);
170                        *right_entity_index += 1;
171                        return Some(pair);
172                    }
173                    *left_entity_index += 1;
174                    *right_entity_index = left_entity_index.saturating_add(1);
175                }
176                None
177            }
178            DescriptorSwapOrder::Explicit(pairs) => pairs.next(),
179        }
180    }
181}
182
183impl<S> MoveCursor<S, DescriptorMoveUnion<S>> for DescriptorSwapMoveCursor<S>
184where
185    S: PlanningSolution + 'static,
186{
187    fn next_candidate(&mut self) -> Option<CandidateId> {
188        while let Some((left_entity_index, right_entity_index)) = self.next_pair() {
189            let Some((left_value, right_value)) = self
190                .legality_index
191                .values_for_swap(left_entity_index, right_entity_index)
192            else {
193                continue;
194            };
195            return Some(self.store.push(DescriptorMoveUnion::Swap(
196                DescriptorSwapMove::new_validated(
197                    self.binding.clone(),
198                    left_entity_index,
199                    left_value,
200                    right_entity_index,
201                    right_value,
202                    self.solution_descriptor.clone(),
203                ),
204            )));
205        }
206        None
207    }
208
209    fn candidate(&self, id: CandidateId) -> Option<MoveCandidateRef<'_, S, DescriptorMoveUnion<S>>> {
210        self.store.candidate(id)
211    }
212
213    fn take_candidate(&mut self, id: CandidateId) -> DescriptorMoveUnion<S> {
214        self.store.take_candidate(id)
215    }
216
217    fn release_candidate(&mut self, id: CandidateId) -> bool {
218        self.store.release_candidate(id)
219    }
220}
221
222impl<S> Iterator for DescriptorSwapMoveCursor<S>
223where
224    S: PlanningSolution + 'static,
225{
226    type Item = DescriptorMoveUnion<S>;
227
228    fn next(&mut self) -> Option<Self::Item> {
229        self.next_owned_candidate()
230    }
231}
232
233#[derive(Clone)]
234pub struct DescriptorChangeMoveSelector<S> {
235    binding: VariableBinding,
236    solution_descriptor: SolutionDescriptor,
237    allows_unassigned: bool,
238    value_candidate_limit: Option<usize>,
239    _phantom: PhantomData<fn() -> S>,
240}
241
242impl<S> Debug for DescriptorChangeMoveSelector<S> {
243    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244        f.debug_struct("DescriptorChangeMoveSelector")
245            .field("binding", &self.binding)
246            .finish()
247    }
248}
249
250impl<S> DescriptorChangeMoveSelector<S> {
251    fn new(
252        binding: VariableBinding,
253        solution_descriptor: SolutionDescriptor,
254        value_candidate_limit: Option<usize>,
255    ) -> Self {
256        let allows_unassigned = binding.allows_unassigned;
257        Self {
258            binding,
259            solution_descriptor,
260            allows_unassigned,
261            value_candidate_limit,
262            _phantom: PhantomData,
263        }
264    }
265}
266
267impl<S> MoveSelector<S, DescriptorMoveUnion<S>> for DescriptorChangeMoveSelector<S>
268where
269    S: PlanningSolution + 'static,
270    S::Score: Score,
271{
272    type Cursor<'a>
273        = DescriptorChangeMoveCursor<S>
274    where
275        Self: 'a;
276
277    fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a> {
278        let count = score_director
279            .entity_count(self.binding.descriptor_index)
280            .unwrap_or(0);
281        let solution = score_director.working_solution() as &dyn Any;
282        let entity_values = (0..count)
283            .map(|entity_index| {
284                let entity = self
285                    .solution_descriptor
286                    .get_entity(solution, self.binding.descriptor_index, entity_index)
287                    .expect("entity lookup failed for change selector");
288                DescriptorEntityValues {
289                    entity_index,
290                    values: self.binding.candidate_values_for_entity_index(
291                        &self.solution_descriptor,
292                        solution,
293                        entity_index,
294                        self.value_candidate_limit,
295                    ),
296                    current_assigned: (self.binding.getter)(entity).is_some(),
297                }
298            })
299            .collect();
300        DescriptorChangeMoveCursor::new(
301            self.binding.clone(),
302            self.solution_descriptor.clone(),
303            entity_values,
304        )
305    }
306
307    fn size<D: Director<S>>(&self, score_director: &D) -> usize {
308        let count = score_director
309            .entity_count(self.binding.descriptor_index)
310            .unwrap_or(0);
311        let mut total = 0;
312        for entity_index in 0..count {
313            let entity = self
314                .solution_descriptor
315                .get_entity(
316                    score_director.working_solution() as &dyn Any,
317                    self.binding.descriptor_index,
318                    entity_index,
319                )
320                .expect("entity lookup failed for change selector");
321            total += self
322                .binding
323                .candidate_values_for_entity_index(
324                    &self.solution_descriptor,
325                    score_director.working_solution() as &dyn Any,
326                    entity_index,
327                    self.value_candidate_limit,
328                )
329                .len()
330                + usize::from(self.allows_unassigned && (self.binding.getter)(entity).is_some());
331        }
332        total
333    }
334}
335
336#[derive(Clone)]
337pub struct DescriptorSwapMoveSelector<S> {
338    binding: VariableBinding,
339    solution_descriptor: SolutionDescriptor,
340    _phantom: PhantomData<fn() -> S>,
341}
342
343impl<S> Debug for DescriptorSwapMoveSelector<S> {
344    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345        f.debug_struct("DescriptorSwapMoveSelector")
346            .field("binding", &self.binding)
347            .finish()
348    }
349}
350
351impl<S> DescriptorSwapMoveSelector<S> {
352    fn new(binding: VariableBinding, solution_descriptor: SolutionDescriptor) -> Self {
353        Self {
354            binding,
355            solution_descriptor,
356            _phantom: PhantomData,
357        }
358    }
359}
360
361impl<S> MoveSelector<S, DescriptorMoveUnion<S>> for DescriptorSwapMoveSelector<S>
362where
363    S: PlanningSolution + 'static,
364    S::Score: Score,
365{
366    type Cursor<'a>
367        = DescriptorSwapMoveCursor<S>
368    where
369        Self: 'a;
370
371    fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a> {
372        let count = score_director
373            .entity_count(self.binding.descriptor_index)
374            .unwrap_or(0);
375        let binding = self.binding.clone();
376        let descriptor = self.solution_descriptor.clone();
377        let solution = score_director.working_solution() as &dyn Any;
378        let legality_index = SwapLegalityIndex::new(
379            &binding,
380            &descriptor,
381            solution,
382            count,
383            "entity lookup failed for swap selector",
384        );
385
386        DescriptorSwapMoveCursor::all(binding, descriptor, legality_index, count)
387    }
388
389    fn size<D: Director<S>>(&self, score_director: &D) -> usize {
390        let count = score_director
391            .entity_count(self.binding.descriptor_index)
392            .unwrap_or(0);
393        let solution = score_director.working_solution() as &dyn Any;
394        let legality_index = SwapLegalityIndex::new(
395            &self.binding,
396            &self.solution_descriptor,
397            solution,
398            count,
399            "entity lookup failed for swap selector",
400        );
401        legality_index.count_legal_pairs()
402    }
403}
404
405#[derive(Clone)]
406pub struct DescriptorNearbyChangeMoveSelector<S> {
407    binding: VariableBinding,
408    solution_descriptor: SolutionDescriptor,
409    max_nearby: usize,
410    value_candidate_limit: Option<usize>,
411    _phantom: PhantomData<fn() -> S>,
412}
413
414impl<S> Debug for DescriptorNearbyChangeMoveSelector<S> {
415    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
416        f.debug_struct("DescriptorNearbyChangeMoveSelector")
417            .field("binding", &self.binding)
418            .field("max_nearby", &self.max_nearby)
419            .field("value_candidate_limit", &self.value_candidate_limit)
420            .finish()
421    }
422}
423
424impl<S> MoveSelector<S, DescriptorMoveUnion<S>> for DescriptorNearbyChangeMoveSelector<S>
425where
426    S: PlanningSolution + 'static,
427    S::Score: Score,
428{
429    type Cursor<'a>
430        = DescriptorChangeMoveCursor<S>
431    where
432        Self: 'a;
433
434    fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a> {
435        let distance_meter = self.binding.nearby_value_distance_meter;
436        let candidate_values = self
437            .binding
438            .nearby_value_candidates
439            .expect("nearby change requires nearby_value_candidates");
440        let solution = score_director.working_solution() as &dyn Any;
441        let count = score_director
442            .entity_count(self.binding.descriptor_index)
443            .unwrap_or(0);
444        let max_nearby = self.max_nearby;
445        let value_candidate_limit = self.value_candidate_limit;
446        let entity_values = (0..count)
447            .map(|entity_index| {
448                let entity = self
449                    .solution_descriptor
450                    .get_entity(solution, self.binding.descriptor_index, entity_index)
451                    .expect("entity lookup failed for nearby change selector");
452                let current_value = (self.binding.getter)(entity);
453                let current_assigned = current_value.is_some();
454                let values = candidate_values(
455                    solution,
456                    entity_index,
457                    self.binding.variable_index,
458                );
459                let limit = value_candidate_limit.unwrap_or(values.len());
460                let mut candidates: Vec<(usize, f64, usize)> = values
461                    .iter()
462                    .copied()
463                    .take(limit)
464                    .enumerate()
465                    .filter_map(|(order, value)| {
466                        if current_value == Some(value) {
467                            return None;
468                        }
469                        let distance = distance_meter
470                            .map(|meter| meter(solution, entity_index, value))
471                            .unwrap_or(order as f64);
472                        distance.is_finite().then_some((value, distance, order))
473                    })
474                    .collect();
475                truncate_nearby_candidates(&mut candidates, max_nearby);
476
477                DescriptorEntityValues {
478                    entity_index,
479                    values: candidates.into_iter().map(|(value, _, _)| value).collect(),
480                    current_assigned,
481                }
482            })
483            .collect();
484        DescriptorChangeMoveCursor::new(
485            self.binding.clone(),
486            self.solution_descriptor.clone(),
487            entity_values,
488        )
489    }
490
491    fn size<D: Director<S>>(&self, score_director: &D) -> usize {
492        self.open_cursor(score_director).count()
493    }
494}
495
496#[derive(Clone)]
497pub struct DescriptorNearbySwapMoveSelector<S> {
498    binding: VariableBinding,
499    solution_descriptor: SolutionDescriptor,
500    max_nearby: usize,
501    _phantom: PhantomData<fn() -> S>,
502}
503
504impl<S> Debug for DescriptorNearbySwapMoveSelector<S> {
505    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
506        f.debug_struct("DescriptorNearbySwapMoveSelector")
507            .field("binding", &self.binding)
508            .field("max_nearby", &self.max_nearby)
509            .finish()
510    }
511}
512
513impl<S> MoveSelector<S, DescriptorMoveUnion<S>> for DescriptorNearbySwapMoveSelector<S>
514where
515    S: PlanningSolution + 'static,
516    S::Score: Score,
517{
518    type Cursor<'a>
519        = DescriptorSwapMoveCursor<S>
520    where
521        Self: 'a;
522
523    fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a> {
524        let distance_meter = self.binding.nearby_entity_distance_meter;
525        let entity_candidates = self
526            .binding
527            .nearby_entity_candidates
528            .expect("nearby swap requires nearby_entity_candidates");
529        let solution = score_director.working_solution() as &dyn Any;
530        let count = score_director
531            .entity_count(self.binding.descriptor_index)
532            .unwrap_or(0);
533        let binding = self.binding.clone();
534        let descriptor = self.solution_descriptor.clone();
535        let max_nearby = self.max_nearby;
536        let legality_index = SwapLegalityIndex::new(
537            &binding,
538            &descriptor,
539            solution,
540            count,
541            "entity lookup failed for nearby swap selector",
542        );
543        let mut pairs = Vec::new();
544        for left_entity_index in 0..count {
545            let mut candidates: Vec<(usize, f64, usize)> = entity_candidates(
546                solution,
547                left_entity_index,
548                binding.variable_index,
549            )
550                .iter()
551                .copied()
552                .enumerate()
553                .filter_map(|(order, right_entity_index)| {
554                    if right_entity_index <= left_entity_index || right_entity_index >= count {
555                        return None;
556                    }
557                    if !legality_index.can_swap(left_entity_index, right_entity_index) {
558                        return None;
559                    }
560                    let distance = distance_meter
561                        .map(|meter| meter(solution, left_entity_index, right_entity_index))
562                        .unwrap_or(order as f64);
563                    distance
564                        .is_finite()
565                        .then_some((right_entity_index, distance, order))
566                })
567                .collect();
568            truncate_nearby_candidates(&mut candidates, max_nearby);
569            for (right_entity_index, _, _) in candidates {
570                let Some((left_value, right_value)) =
571                    legality_index.values_for_swap(left_entity_index, right_entity_index)
572                else {
573                    continue;
574                };
575                let _ = (left_value, right_value);
576                pairs.push((left_entity_index, right_entity_index));
577            }
578        }
579        DescriptorSwapMoveCursor::explicit(binding, descriptor, legality_index, pairs)
580    }
581
582    fn size<D: Director<S>>(&self, score_director: &D) -> usize {
583        self.open_cursor(score_director).count()
584    }
585}