Skip to main content

solverforge_solver/descriptor_standard/
selectors.rs

1use std::any::Any;
2use std::fmt::{self, Debug};
3use std::marker::PhantomData;
4
5use solverforge_config::MoveSelectorConfig;
6use solverforge_core::domain::{PlanningSolution, SolutionDescriptor};
7use solverforge_scoring::Director;
8
9use crate::heuristic::selector::decorator::VecUnionSelector;
10use crate::heuristic::selector::move_selector::MoveSelector;
11
12use super::bindings::{collect_bindings, find_binding, VariableBinding};
13use super::move_types::{DescriptorChangeMove, DescriptorEitherMove, DescriptorSwapMove};
14
15#[derive(Clone)]
16pub struct DescriptorChangeMoveSelector<S> {
17    binding: VariableBinding,
18    solution_descriptor: SolutionDescriptor,
19    _phantom: PhantomData<fn() -> S>,
20}
21
22impl<S> Debug for DescriptorChangeMoveSelector<S> {
23    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24        f.debug_struct("DescriptorChangeMoveSelector")
25            .field("binding", &self.binding)
26            .finish()
27    }
28}
29
30impl<S> DescriptorChangeMoveSelector<S> {
31    fn new(binding: VariableBinding, solution_descriptor: SolutionDescriptor) -> Self {
32        Self {
33            binding,
34            solution_descriptor,
35            _phantom: PhantomData,
36        }
37    }
38}
39
40impl<S> MoveSelector<S, DescriptorEitherMove<S>> for DescriptorChangeMoveSelector<S>
41where
42    S: PlanningSolution + 'static,
43{
44    fn open_cursor<'a, D: Director<S>>(
45        &'a self,
46        score_director: &D,
47    ) -> impl Iterator<Item = DescriptorEitherMove<S>> + 'a {
48        let count = score_director
49            .entity_count(self.binding.descriptor_index)
50            .unwrap_or(0);
51        let descriptor = self.solution_descriptor.clone();
52        let binding = self.binding.clone();
53        let solution = score_director.working_solution() as &dyn Any;
54        let moves: Vec<_> = (0..count)
55            .flat_map(move |entity_index| {
56                let entity = descriptor
57                    .get_entity(solution, binding.descriptor_index, entity_index)
58                    .expect("entity lookup failed for change selector");
59                binding
60                    .values_for_entity(&descriptor, solution, entity)
61                    .into_iter()
62                    .map({
63                        let binding = binding.clone();
64                        let descriptor = descriptor.clone();
65                        move |value| {
66                            DescriptorEitherMove::Change(DescriptorChangeMove::new(
67                                binding.clone(),
68                                entity_index,
69                                Some(value),
70                                descriptor.clone(),
71                            ))
72                        }
73                    })
74            })
75            .collect();
76        moves.into_iter()
77    }
78
79    fn size<D: Director<S>>(&self, score_director: &D) -> usize {
80        let count = score_director
81            .entity_count(self.binding.descriptor_index)
82            .unwrap_or(0);
83        let mut total = 0;
84        for entity_index in 0..count {
85            let entity = self
86                .solution_descriptor
87                .get_entity(
88                    score_director.working_solution() as &dyn Any,
89                    self.binding.descriptor_index,
90                    entity_index,
91                )
92                .expect("entity lookup failed for change selector");
93            total += self
94                .binding
95                .values_for_entity(
96                    &self.solution_descriptor,
97                    score_director.working_solution() as &dyn Any,
98                    entity,
99                )
100                .len();
101        }
102        total
103    }
104}
105
106#[derive(Clone)]
107pub struct DescriptorSwapMoveSelector<S> {
108    binding: VariableBinding,
109    solution_descriptor: SolutionDescriptor,
110    _phantom: PhantomData<fn() -> S>,
111}
112
113impl<S> Debug for DescriptorSwapMoveSelector<S> {
114    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115        f.debug_struct("DescriptorSwapMoveSelector")
116            .field("binding", &self.binding)
117            .finish()
118    }
119}
120
121impl<S> DescriptorSwapMoveSelector<S> {
122    fn new(binding: VariableBinding, solution_descriptor: SolutionDescriptor) -> Self {
123        Self {
124            binding,
125            solution_descriptor,
126            _phantom: PhantomData,
127        }
128    }
129}
130
131impl<S> MoveSelector<S, DescriptorEitherMove<S>> for DescriptorSwapMoveSelector<S>
132where
133    S: PlanningSolution + 'static,
134{
135    fn open_cursor<'a, D: Director<S>>(
136        &'a self,
137        score_director: &D,
138    ) -> impl Iterator<Item = DescriptorEitherMove<S>> + 'a {
139        let count = score_director
140            .entity_count(self.binding.descriptor_index)
141            .unwrap_or(0);
142        let binding = self.binding.clone();
143        let descriptor = self.solution_descriptor.clone();
144        let moves: Vec<_> = (0..count)
145            .flat_map(move |left_entity_index| {
146                ((left_entity_index + 1)..count).map({
147                    let binding = binding.clone();
148                    let descriptor = descriptor.clone();
149                    move |right_entity_index| {
150                        DescriptorEitherMove::Swap(DescriptorSwapMove::new(
151                            binding.clone(),
152                            left_entity_index,
153                            right_entity_index,
154                            descriptor.clone(),
155                        ))
156                    }
157                })
158            })
159            .collect();
160        moves.into_iter()
161    }
162
163    fn size<D: Director<S>>(&self, score_director: &D) -> usize {
164        let count = score_director
165            .entity_count(self.binding.descriptor_index)
166            .unwrap_or(0);
167        count.saturating_mul(count.saturating_sub(1)) / 2
168    }
169}
170
171#[derive(Clone)]
172pub enum DescriptorLeafSelector<S> {
173    Change(DescriptorChangeMoveSelector<S>),
174    Swap(DescriptorSwapMoveSelector<S>),
175}
176
177impl<S> Debug for DescriptorLeafSelector<S>
178where
179    S: PlanningSolution,
180{
181    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182        match self {
183            Self::Change(selector) => selector.fmt(f),
184            Self::Swap(selector) => selector.fmt(f),
185        }
186    }
187}
188
189impl<S> MoveSelector<S, DescriptorEitherMove<S>> for DescriptorLeafSelector<S>
190where
191    S: PlanningSolution + 'static,
192{
193    fn open_cursor<'a, D: Director<S>>(
194        &'a self,
195        score_director: &D,
196    ) -> impl Iterator<Item = DescriptorEitherMove<S>> + 'a {
197        enum DescriptorLeafIter<A, B> {
198            Change(A),
199            Swap(B),
200        }
201
202        impl<T, A, B> Iterator for DescriptorLeafIter<A, B>
203        where
204            A: Iterator<Item = T>,
205            B: Iterator<Item = T>,
206        {
207            type Item = T;
208
209            fn next(&mut self) -> Option<Self::Item> {
210                match self {
211                    Self::Change(iter) => iter.next(),
212                    Self::Swap(iter) => iter.next(),
213                }
214            }
215        }
216
217        match self {
218            Self::Change(selector) => {
219                DescriptorLeafIter::Change(selector.open_cursor(score_director))
220            }
221            Self::Swap(selector) => DescriptorLeafIter::Swap(selector.open_cursor(score_director)),
222        }
223    }
224
225    fn size<D: Director<S>>(&self, score_director: &D) -> usize {
226        match self {
227            Self::Change(selector) => selector.size(score_director),
228            Self::Swap(selector) => selector.size(score_director),
229        }
230    }
231}
232
233fn collect_descriptor_leaf_selectors<S>(
234    config: Option<&MoveSelectorConfig>,
235    descriptor: &SolutionDescriptor,
236) -> Vec<DescriptorLeafSelector<S>>
237where
238    S: PlanningSolution + 'static,
239{
240    let bindings = collect_bindings(descriptor);
241    let mut leaves = Vec::new();
242
243    fn collect<S>(
244        cfg: &MoveSelectorConfig,
245        descriptor: &SolutionDescriptor,
246        bindings: &[VariableBinding],
247        leaves: &mut Vec<DescriptorLeafSelector<S>>,
248    ) where
249        S: PlanningSolution + 'static,
250    {
251        match cfg {
252            MoveSelectorConfig::ChangeMoveSelector(change) => {
253                let matched = find_binding(
254                    bindings,
255                    change.target.entity_class.as_deref(),
256                    change.target.variable_name.as_deref(),
257                );
258                assert!(
259                    !matched.is_empty(),
260                    "change_move selector matched no standard planning variables for entity_class={:?} variable_name={:?}",
261                    change.target.entity_class,
262                    change.target.variable_name
263                );
264                for binding in matched {
265                    leaves.push(DescriptorLeafSelector::Change(
266                        DescriptorChangeMoveSelector::new(binding, descriptor.clone()),
267                    ));
268                }
269            }
270            MoveSelectorConfig::SwapMoveSelector(swap) => {
271                let matched = find_binding(
272                    bindings,
273                    swap.target.entity_class.as_deref(),
274                    swap.target.variable_name.as_deref(),
275                );
276                assert!(
277                    !matched.is_empty(),
278                    "swap_move selector matched no standard planning variables for entity_class={:?} variable_name={:?}",
279                    swap.target.entity_class,
280                    swap.target.variable_name
281                );
282                for binding in matched {
283                    leaves.push(DescriptorLeafSelector::Swap(
284                        DescriptorSwapMoveSelector::new(binding, descriptor.clone()),
285                    ));
286                }
287            }
288            MoveSelectorConfig::UnionMoveSelector(union) => {
289                for child in &union.selectors {
290                    collect(child, descriptor, bindings, leaves);
291                }
292            }
293            MoveSelectorConfig::SelectedCountLimitMoveSelector(_) => {
294                panic!(
295                    "selected_count_limit_move_selector must be handled by the canonical runtime"
296                );
297            }
298            MoveSelectorConfig::ListChangeMoveSelector(_)
299            | MoveSelectorConfig::NearbyListChangeMoveSelector(_)
300            | MoveSelectorConfig::ListSwapMoveSelector(_)
301            | MoveSelectorConfig::NearbyListSwapMoveSelector(_)
302            | MoveSelectorConfig::SubListChangeMoveSelector(_)
303            | MoveSelectorConfig::SubListSwapMoveSelector(_)
304            | MoveSelectorConfig::ListReverseMoveSelector(_)
305            | MoveSelectorConfig::KOptMoveSelector(_)
306            | MoveSelectorConfig::ListRuinMoveSelector(_) => {
307                panic!("list move selector configured against a standard-variable context");
308            }
309            MoveSelectorConfig::CartesianProductMoveSelector(_) => {
310                panic!("cartesian_product move selectors are not supported in the canonical solver path");
311            }
312        }
313    }
314
315    match config {
316        Some(cfg) => collect(cfg, descriptor, &bindings, &mut leaves),
317        None => {
318            for binding in bindings {
319                leaves.push(DescriptorLeafSelector::Change(
320                    DescriptorChangeMoveSelector::new(binding.clone(), descriptor.clone()),
321                ));
322                leaves.push(DescriptorLeafSelector::Swap(
323                    DescriptorSwapMoveSelector::new(binding, descriptor.clone()),
324                ));
325            }
326        }
327    }
328
329    assert!(
330        !leaves.is_empty(),
331        "move selector configuration produced no standard neighborhoods"
332    );
333
334    leaves
335}
336
337pub fn build_descriptor_move_selector<S>(
338    config: Option<&MoveSelectorConfig>,
339    descriptor: &SolutionDescriptor,
340) -> VecUnionSelector<S, DescriptorEitherMove<S>, DescriptorLeafSelector<S>>
341where
342    S: PlanningSolution + 'static,
343{
344    VecUnionSelector::new(collect_descriptor_leaf_selectors(config, descriptor))
345}