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 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}