solverforge_solver/descriptor_standard/
selectors.rs1use 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}