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