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