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