Skip to main content

solverforge_solver/descriptor_scalar/selectors/
change_swap.rs

1#[derive(Clone)]
2pub struct DescriptorChangeMoveSelector<S> {
3    binding: VariableBinding,
4    solution_descriptor: SolutionDescriptor,
5    allows_unassigned: bool,
6    _phantom: PhantomData<fn() -> S>,
7}
8
9impl<S> Debug for DescriptorChangeMoveSelector<S> {
10    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
11        f.debug_struct("DescriptorChangeMoveSelector")
12            .field("binding", &self.binding)
13            .finish()
14    }
15}
16
17impl<S> DescriptorChangeMoveSelector<S> {
18    fn new(binding: VariableBinding, solution_descriptor: SolutionDescriptor) -> Self {
19        let allows_unassigned = binding.allows_unassigned;
20        Self {
21            binding,
22            solution_descriptor,
23            allows_unassigned,
24            _phantom: PhantomData,
25        }
26    }
27}
28
29impl<S> MoveSelector<S, DescriptorScalarMoveUnion<S>> for DescriptorChangeMoveSelector<S>
30where
31    S: PlanningSolution + 'static,
32    S::Score: Score,
33{
34    type Cursor<'a>
35        = ArenaMoveCursor<S, DescriptorScalarMoveUnion<S>>
36    where
37        Self: 'a;
38
39    fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a> {
40        let count = score_director
41            .entity_count(self.binding.descriptor_index)
42            .unwrap_or(0);
43        let descriptor = self.solution_descriptor.clone();
44        let binding = self.binding.clone();
45        let allows_unassigned = self.allows_unassigned;
46        let solution = score_director.working_solution() as &dyn Any;
47        let moves: Vec<_> = (0..count)
48            .flat_map(move |entity_index| {
49                let entity = descriptor
50                    .get_entity(solution, binding.descriptor_index, entity_index)
51                    .expect("entity lookup failed for change selector");
52                let current_value = (binding.getter)(entity);
53                let unassign_move = (allows_unassigned && current_value.is_some()).then({
54                    let binding = binding.clone();
55                    let descriptor = descriptor.clone();
56                    move || {
57                        DescriptorScalarMoveUnion::Change(DescriptorChangeMove::new(
58                            binding.clone(),
59                            entity_index,
60                            None,
61                            descriptor.clone(),
62                        ))
63                    }
64                });
65                binding
66                    .values_for_entity(&descriptor, solution, entity)
67                    .into_iter()
68                    .map({
69                        let binding = binding.clone();
70                        let descriptor = descriptor.clone();
71                        move |value| {
72                            DescriptorScalarMoveUnion::Change(DescriptorChangeMove::new(
73                                binding.clone(),
74                                entity_index,
75                                Some(value),
76                                descriptor.clone(),
77                            ))
78                        }
79                    })
80                    .chain(unassign_move)
81            })
82            .collect();
83        ArenaMoveCursor::from_moves(moves)
84    }
85
86    fn size<D: Director<S>>(&self, score_director: &D) -> usize {
87        let count = score_director
88            .entity_count(self.binding.descriptor_index)
89            .unwrap_or(0);
90        let mut total = 0;
91        for entity_index in 0..count {
92            let entity = self
93                .solution_descriptor
94                .get_entity(
95                    score_director.working_solution() as &dyn Any,
96                    self.binding.descriptor_index,
97                    entity_index,
98                )
99                .expect("entity lookup failed for change selector");
100            total += self
101                .binding
102                .values_for_entity(
103                    &self.solution_descriptor,
104                    score_director.working_solution() as &dyn Any,
105                    entity,
106                )
107                .len()
108                + usize::from(self.allows_unassigned && (self.binding.getter)(entity).is_some());
109        }
110        total
111    }
112}
113
114#[derive(Clone)]
115pub struct DescriptorSwapMoveSelector<S> {
116    binding: VariableBinding,
117    solution_descriptor: SolutionDescriptor,
118    _phantom: PhantomData<fn() -> S>,
119}
120
121impl<S> Debug for DescriptorSwapMoveSelector<S> {
122    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123        f.debug_struct("DescriptorSwapMoveSelector")
124            .field("binding", &self.binding)
125            .finish()
126    }
127}
128
129impl<S> DescriptorSwapMoveSelector<S> {
130    fn new(binding: VariableBinding, solution_descriptor: SolutionDescriptor) -> Self {
131        Self {
132            binding,
133            solution_descriptor,
134            _phantom: PhantomData,
135        }
136    }
137}
138
139impl<S> MoveSelector<S, DescriptorScalarMoveUnion<S>> for DescriptorSwapMoveSelector<S>
140where
141    S: PlanningSolution + 'static,
142    S::Score: Score,
143{
144    type Cursor<'a>
145        = ArenaMoveCursor<S, DescriptorScalarMoveUnion<S>>
146    where
147        Self: 'a;
148
149    fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a> {
150        let count = score_director
151            .entity_count(self.binding.descriptor_index)
152            .unwrap_or(0);
153        let binding = self.binding.clone();
154        let descriptor = self.solution_descriptor.clone();
155        let solution = score_director.working_solution() as &dyn Any;
156        let legality_index = SwapLegalityIndex::new(
157            &binding,
158            &descriptor,
159            solution,
160            count,
161            "entity lookup failed for swap selector",
162        );
163
164        let mut moves = Vec::new();
165        for left_entity_index in 0..count {
166            for right_entity_index in (left_entity_index + 1)..count {
167                if let Some((left_value, right_value)) =
168                    legality_index.values_for_swap(left_entity_index, right_entity_index)
169                {
170                    moves.push(DescriptorScalarMoveUnion::Swap(
171                        DescriptorSwapMove::new_validated(
172                            binding.clone(),
173                            left_entity_index,
174                            left_value,
175                            right_entity_index,
176                            right_value,
177                            descriptor.clone(),
178                        ),
179                    ));
180                }
181            }
182        }
183        ArenaMoveCursor::from_moves(moves)
184    }
185
186    fn size<D: Director<S>>(&self, score_director: &D) -> usize {
187        let count = score_director
188            .entity_count(self.binding.descriptor_index)
189            .unwrap_or(0);
190        let solution = score_director.working_solution() as &dyn Any;
191        let legality_index = SwapLegalityIndex::new(
192            &self.binding,
193            &self.solution_descriptor,
194            solution,
195            count,
196            "entity lookup failed for swap selector",
197        );
198        legality_index.count_legal_pairs()
199    }
200}
201
202#[derive(Clone)]
203pub struct DescriptorNearbyChangeMoveSelector<S> {
204    binding: VariableBinding,
205    solution_descriptor: SolutionDescriptor,
206    max_nearby: usize,
207    _phantom: PhantomData<fn() -> S>,
208}
209
210impl<S> Debug for DescriptorNearbyChangeMoveSelector<S> {
211    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212        f.debug_struct("DescriptorNearbyChangeMoveSelector")
213            .field("binding", &self.binding)
214            .field("max_nearby", &self.max_nearby)
215            .finish()
216    }
217}
218
219impl<S> MoveSelector<S, DescriptorScalarMoveUnion<S>> for DescriptorNearbyChangeMoveSelector<S>
220where
221    S: PlanningSolution + 'static,
222    S::Score: Score,
223{
224    type Cursor<'a>
225        = ArenaMoveCursor<S, DescriptorScalarMoveUnion<S>>
226    where
227        Self: 'a;
228
229    fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a> {
230        let distance_meter = self
231            .binding
232            .nearby_value_distance_meter
233            .expect("nearby change requires a nearby value distance meter");
234        let solution = score_director.working_solution() as &dyn Any;
235        let count = score_director
236            .entity_count(self.binding.descriptor_index)
237            .unwrap_or(0);
238        let binding = self.binding.clone();
239        let descriptor = self.solution_descriptor.clone();
240        let max_nearby = self.max_nearby;
241        let moves: Vec<_> = (0..count)
242            .flat_map(move |entity_index| {
243                let entity = descriptor
244                    .get_entity(solution, binding.descriptor_index, entity_index)
245                    .expect("entity lookup failed for nearby change selector");
246                let current_value = (binding.getter)(entity);
247                let current_assigned = current_value.is_some();
248                let mut candidates: Vec<(usize, f64, usize)> = binding
249                    .values_for_entity(&descriptor, solution, entity)
250                    .into_iter()
251                    .enumerate()
252                    .filter_map(|(order, value)| {
253                        if current_value == Some(value) {
254                            return None;
255                        }
256                        let distance = distance_meter(solution, entity_index, value);
257                        distance.is_finite().then_some((value, distance, order))
258                    })
259                    .collect();
260                truncate_nearby_candidates(&mut candidates, max_nearby);
261
262                let candidate_moves = candidates.into_iter().map({
263                    let binding = binding.clone();
264                    let descriptor = descriptor.clone();
265                    move |(value, _, _)| {
266                        DescriptorScalarMoveUnion::Change(DescriptorChangeMove::new(
267                            binding.clone(),
268                            entity_index,
269                            Some(value),
270                            descriptor.clone(),
271                        ))
272                    }
273                });
274                let unassign = (binding.allows_unassigned && current_assigned).then({
275                    let binding = binding.clone();
276                    let descriptor = descriptor.clone();
277                    move || {
278                        DescriptorScalarMoveUnion::Change(DescriptorChangeMove::new(
279                            binding.clone(),
280                            entity_index,
281                            None,
282                            descriptor.clone(),
283                        ))
284                    }
285                });
286                candidate_moves.chain(unassign)
287            })
288            .collect();
289        ArenaMoveCursor::from_moves(moves)
290    }
291
292    fn size<D: Director<S>>(&self, score_director: &D) -> usize {
293        self.open_cursor(score_director).count()
294    }
295}
296
297#[derive(Clone)]
298pub struct DescriptorNearbySwapMoveSelector<S> {
299    binding: VariableBinding,
300    solution_descriptor: SolutionDescriptor,
301    max_nearby: usize,
302    _phantom: PhantomData<fn() -> S>,
303}
304
305impl<S> Debug for DescriptorNearbySwapMoveSelector<S> {
306    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
307        f.debug_struct("DescriptorNearbySwapMoveSelector")
308            .field("binding", &self.binding)
309            .field("max_nearby", &self.max_nearby)
310            .finish()
311    }
312}
313
314impl<S> MoveSelector<S, DescriptorScalarMoveUnion<S>> for DescriptorNearbySwapMoveSelector<S>
315where
316    S: PlanningSolution + 'static,
317    S::Score: Score,
318{
319    type Cursor<'a>
320        = ArenaMoveCursor<S, DescriptorScalarMoveUnion<S>>
321    where
322        Self: 'a;
323
324    fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a> {
325        let distance_meter = self
326            .binding
327            .nearby_entity_distance_meter
328            .expect("nearby swap requires a nearby entity distance meter");
329        let solution = score_director.working_solution() as &dyn Any;
330        let count = score_director
331            .entity_count(self.binding.descriptor_index)
332            .unwrap_or(0);
333        let binding = self.binding.clone();
334        let descriptor = self.solution_descriptor.clone();
335        let max_nearby = self.max_nearby;
336        let legality_index = SwapLegalityIndex::new(
337            &binding,
338            &descriptor,
339            solution,
340            count,
341            "entity lookup failed for nearby swap selector",
342        );
343        let mut moves = Vec::new();
344        for left_entity_index in 0..count {
345            let mut candidates: Vec<(usize, f64, usize)> = ((left_entity_index + 1)..count)
346                .enumerate()
347                .filter_map(|(order, right_entity_index)| {
348                    if !legality_index.can_swap(left_entity_index, right_entity_index) {
349                        return None;
350                    }
351                    let distance = distance_meter(solution, left_entity_index, right_entity_index);
352                    distance
353                        .is_finite()
354                        .then_some((right_entity_index, distance, order))
355                })
356                .collect();
357            truncate_nearby_candidates(&mut candidates, max_nearby);
358            for (right_entity_index, _, _) in candidates {
359                let Some((left_value, right_value)) =
360                    legality_index.values_for_swap(left_entity_index, right_entity_index)
361                else {
362                    continue;
363                };
364                moves.push(DescriptorScalarMoveUnion::Swap(
365                    DescriptorSwapMove::new_validated(
366                        binding.clone(),
367                        left_entity_index,
368                        left_value,
369                        right_entity_index,
370                        right_value,
371                        descriptor.clone(),
372                    ),
373                ));
374            }
375        }
376        ArenaMoveCursor::from_moves(moves)
377    }
378
379    fn size<D: Director<S>>(&self, score_director: &D) -> usize {
380        self.open_cursor(score_director).count()
381    }
382}
383