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