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