Skip to main content

solverforge_solver/descriptor_scalar/construction/
placer.rs

1use std::any::Any;
2use std::fmt::{self, Debug};
3use std::marker::PhantomData;
4
5use solverforge_config::ConstructionHeuristicType;
6use solverforge_core::domain::{PlanningSolution, SolutionDescriptor};
7use solverforge_core::score::Score;
8use solverforge_scoring::Director;
9
10use crate::heuristic::selector::EntityReference;
11use crate::phase::construction::{
12    BestFitForager, ConstructionHeuristicPhase, EntityPlacer, FirstFitForager, Placement,
13    StrongestFitForager, WeakestFitForager,
14};
15use crate::scope::{ProgressCallback, SolverScope};
16
17use super::super::bindings::ResolvedVariableBinding;
18use super::super::move_types::{DescriptorChangeMove, DescriptorScalarMoveUnion};
19
20pub enum DescriptorConstruction<S: PlanningSolution> {
21    FirstFit(
22        ConstructionHeuristicPhase<
23            S,
24            DescriptorScalarMoveUnion<S>,
25            DescriptorEntityPlacer<S>,
26            FirstFitForager<S, DescriptorScalarMoveUnion<S>>,
27        >,
28    ),
29    BestFit(
30        ConstructionHeuristicPhase<
31            S,
32            DescriptorScalarMoveUnion<S>,
33            DescriptorEntityPlacer<S>,
34            BestFitForager<S, DescriptorScalarMoveUnion<S>>,
35        >,
36    ),
37    WeakestFit(
38        ConstructionHeuristicPhase<
39            S,
40            DescriptorScalarMoveUnion<S>,
41            DescriptorEntityPlacer<S>,
42            WeakestFitForager<S, DescriptorScalarMoveUnion<S>>,
43        >,
44    ),
45    StrongestFit(
46        ConstructionHeuristicPhase<
47            S,
48            DescriptorScalarMoveUnion<S>,
49            DescriptorEntityPlacer<S>,
50            StrongestFitForager<S, DescriptorScalarMoveUnion<S>>,
51        >,
52    ),
53}
54
55impl<S: PlanningSolution> Debug for DescriptorConstruction<S> {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        match self {
58            Self::FirstFit(phase) => write!(f, "DescriptorConstruction::FirstFit({phase:?})"),
59            Self::BestFit(phase) => write!(f, "DescriptorConstruction::BestFit({phase:?})"),
60            Self::WeakestFit(phase) => {
61                write!(f, "DescriptorConstruction::WeakestFit({phase:?})")
62            }
63            Self::StrongestFit(phase) => {
64                write!(f, "DescriptorConstruction::StrongestFit({phase:?})")
65            }
66        }
67    }
68}
69
70impl<S, D, ProgressCb> crate::phase::Phase<S, D, ProgressCb> for DescriptorConstruction<S>
71where
72    S: PlanningSolution + 'static,
73    D: Director<S>,
74    ProgressCb: ProgressCallback<S>,
75{
76    fn solve(&mut self, solver_scope: &mut SolverScope<'_, S, D, ProgressCb>) {
77        match self {
78            Self::FirstFit(phase) => phase.solve(solver_scope),
79            Self::BestFit(phase) => phase.solve(solver_scope),
80            Self::WeakestFit(phase) => phase.solve(solver_scope),
81            Self::StrongestFit(phase) => phase.solve(solver_scope),
82        }
83    }
84
85    fn phase_type_name(&self) -> &'static str {
86        "DescriptorConstruction"
87    }
88}
89
90#[derive(Clone, Copy, Debug, PartialEq, Eq)]
91pub(super) enum EntityOrder {
92    Canonical,
93    AscendingKey,
94    DescendingKey,
95}
96
97#[derive(Clone, Copy, Debug, PartialEq, Eq)]
98pub(super) enum ValueOrder {
99    Canonical,
100    AscendingKey,
101}
102
103#[derive(Clone)]
104pub struct DescriptorEntityPlacer<S> {
105    bindings: Vec<ResolvedVariableBinding<S>>,
106    solution_descriptor: SolutionDescriptor,
107    entity_order: EntityOrder,
108    value_order: ValueOrder,
109    value_candidate_limit: Option<usize>,
110    _phantom: PhantomData<fn() -> S>,
111}
112
113impl<S> Debug for DescriptorEntityPlacer<S> {
114    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115        f.debug_struct("DescriptorEntityPlacer")
116            .field("bindings", &self.bindings)
117            .field("entity_order", &self.entity_order)
118            .field("value_order", &self.value_order)
119            .field("value_candidate_limit", &self.value_candidate_limit)
120            .finish()
121    }
122}
123
124impl<S> DescriptorEntityPlacer<S>
125where
126    S: PlanningSolution + 'static,
127{
128    pub(super) fn new(
129        bindings: Vec<ResolvedVariableBinding<S>>,
130        solution_descriptor: SolutionDescriptor,
131        entity_order: EntityOrder,
132        value_order: ValueOrder,
133        value_candidate_limit: Option<usize>,
134    ) -> Self {
135        Self {
136            bindings,
137            solution_descriptor,
138            entity_order,
139            value_order,
140            value_candidate_limit,
141            _phantom: PhantomData,
142        }
143    }
144
145    fn entity_order_key(
146        &self,
147        binding: &ResolvedVariableBinding<S>,
148        solution: &S,
149        entity_index: usize,
150    ) -> i64 {
151        binding.entity_order_key(solution, entity_index).unwrap_or_else(|| {
152            unreachable!(
153                "validated descriptor scalar construction must provide construction_entity_order_key for {}.{}",
154                binding.entity_type_name,
155                binding.variable_name
156            )
157        })
158    }
159
160    fn value_order_key(
161        &self,
162        binding: &ResolvedVariableBinding<S>,
163        solution: &S,
164        entity_index: usize,
165        value: usize,
166    ) -> i64 {
167        binding
168            .value_order_key(solution, entity_index, value)
169            .unwrap_or_else(|| {
170                unreachable!(
171                    "validated descriptor scalar construction must provide construction_value_order_key for {}.{}",
172                    binding.entity_type_name,
173                    binding.variable_name
174                )
175            })
176    }
177
178    fn ordered_entity_indices<D: Director<S>>(
179        &self,
180        binding: &ResolvedVariableBinding<S>,
181        score_director: &D,
182    ) -> Vec<usize> {
183        let count = score_director
184            .entity_count(binding.descriptor_index)
185            .unwrap_or(0);
186        let mut entity_indices: Vec<_> = (0..count).collect();
187        if self.entity_order != EntityOrder::Canonical {
188            let solution = score_director.working_solution();
189            entity_indices.sort_by(|left, right| {
190                let left_key = self.entity_order_key(binding, solution, *left);
191                let right_key = self.entity_order_key(binding, solution, *right);
192                match self.entity_order {
193                    EntityOrder::Canonical => left.cmp(right),
194                    EntityOrder::AscendingKey => left_key.cmp(&right_key).then(left.cmp(right)),
195                    EntityOrder::DescendingKey => right_key.cmp(&left_key).then(left.cmp(right)),
196                }
197            });
198        }
199        entity_indices
200    }
201
202    fn ordered_candidate_values(
203        &self,
204        binding: &ResolvedVariableBinding<S>,
205        solution: &S,
206        entity_index: usize,
207    ) -> Vec<usize> {
208        let mut values: Vec<_> = binding
209            .candidate_values_for_entity_index(
210                &self.solution_descriptor,
211                solution as &dyn Any,
212                entity_index,
213                self.value_candidate_limit,
214            )
215            .into_iter()
216            .enumerate()
217            .collect();
218        if self.value_order != ValueOrder::Canonical {
219            values.sort_by(|(left_order, left_value), (right_order, right_value)| {
220                let left_key = self.value_order_key(binding, solution, entity_index, *left_value);
221                let right_key = self.value_order_key(binding, solution, entity_index, *right_value);
222                match self.value_order {
223                    ValueOrder::Canonical => left_order.cmp(right_order),
224                    ValueOrder::AscendingKey => {
225                        left_key.cmp(&right_key).then(left_order.cmp(right_order))
226                    }
227                }
228            });
229        }
230        values.into_iter().map(|(_, value)| value).collect()
231    }
232
233    fn descriptor_change_move(
234        &self,
235        binding: &ResolvedVariableBinding<S>,
236        entity_index: usize,
237        value: usize,
238    ) -> DescriptorScalarMoveUnion<S> {
239        let mut mov = DescriptorChangeMove::new(
240            binding.clone_binding(),
241            entity_index,
242            Some(value),
243            self.solution_descriptor.clone(),
244        );
245        if let Some(order_key) = binding.runtime_value_order_key() {
246            mov = mov.with_construction_value_order_key(order_key);
247        }
248        DescriptorScalarMoveUnion::Change(mov)
249    }
250
251    fn placement_for_entity(
252        &self,
253        binding: &ResolvedVariableBinding<S>,
254        solution: &S,
255        entity_index: usize,
256    ) -> Option<Placement<S, DescriptorScalarMoveUnion<S>>> {
257        let moves = self
258            .ordered_candidate_values(binding, solution, entity_index)
259            .into_iter()
260            .map(|value| self.descriptor_change_move(binding, entity_index, value))
261            .collect::<Vec<_>>();
262
263        if moves.is_empty() {
264            return None;
265        }
266
267        Some(
268            Placement::new(
269                EntityReference::new(binding.descriptor_index, entity_index),
270                moves,
271            )
272            .with_slot_id(binding.slot_id(entity_index))
273            .with_keep_current_legal(binding.allows_unassigned),
274        )
275    }
276}
277
278impl<S> EntityPlacer<S, DescriptorScalarMoveUnion<S>> for DescriptorEntityPlacer<S>
279where
280    S: PlanningSolution + 'static,
281{
282    fn get_placements<D: Director<S>>(
283        &self,
284        score_director: &D,
285    ) -> Vec<Placement<S, DescriptorScalarMoveUnion<S>>> {
286        let mut placements = Vec::new();
287        let solution = score_director.working_solution();
288        let erased_solution = solution as &dyn Any;
289
290        for binding in &self.bindings {
291            for entity_index in self.ordered_entity_indices(binding, score_director) {
292                let entity = self
293                    .solution_descriptor
294                    .get_entity(erased_solution, binding.descriptor_index, entity_index)
295                    .expect("entity lookup failed for descriptor construction");
296                if (binding.getter)(entity).is_some() {
297                    continue;
298                }
299
300                if let Some(placement) = self.placement_for_entity(binding, solution, entity_index)
301                {
302                    placements.push(placement);
303                }
304            }
305        }
306
307        placements
308    }
309
310    fn get_next_placement<D, IsCompleted>(
311        &self,
312        score_director: &D,
313        mut is_completed: IsCompleted,
314    ) -> Option<(Placement<S, DescriptorScalarMoveUnion<S>>, u64)>
315    where
316        D: Director<S>,
317        IsCompleted: FnMut(usize, usize) -> bool,
318    {
319        let solution = score_director.working_solution();
320        let erased_solution = solution as &dyn Any;
321
322        for binding in &self.bindings {
323            for entity_index in self.ordered_entity_indices(binding, score_director) {
324                let slot_id = binding.slot_id(entity_index);
325                if is_completed(slot_id.binding_index(), slot_id.entity_index()) {
326                    continue;
327                }
328
329                let entity = self
330                    .solution_descriptor
331                    .get_entity(erased_solution, binding.descriptor_index, entity_index)
332                    .expect("entity lookup failed for descriptor construction");
333                if (binding.getter)(entity).is_some() {
334                    continue;
335                }
336
337                if let Some(placement) = self.placement_for_entity(binding, solution, entity_index)
338                {
339                    let generated_moves = u64::try_from(placement.moves.len()).unwrap_or(u64::MAX);
340                    return Some((placement, generated_moves));
341                }
342            }
343        }
344
345        None
346    }
347}
348
349pub(super) fn descriptor_scalar_move_strength<S>(
350    mov: &DescriptorScalarMoveUnion<S>,
351    solution: &S,
352) -> i64
353where
354    S: PlanningSolution + 'static,
355    S::Score: Score,
356{
357    match mov {
358        DescriptorScalarMoveUnion::Change(mov) => mov.live_value_order_key(solution).unwrap_or(0),
359        _ => 0,
360    }
361}
362
363pub(super) fn entity_order_for_heuristic(heuristic: ConstructionHeuristicType) -> EntityOrder {
364    match heuristic {
365        ConstructionHeuristicType::FirstFitDecreasing
366        | ConstructionHeuristicType::WeakestFitDecreasing
367        | ConstructionHeuristicType::StrongestFitDecreasing => EntityOrder::DescendingKey,
368        ConstructionHeuristicType::AllocateEntityFromQueue => EntityOrder::AscendingKey,
369        _ => EntityOrder::Canonical,
370    }
371}
372
373pub(super) fn value_order_for_heuristic(heuristic: ConstructionHeuristicType) -> ValueOrder {
374    match heuristic {
375        ConstructionHeuristicType::AllocateToValueFromQueue => ValueOrder::AscendingKey,
376        _ => ValueOrder::Canonical,
377    }
378}
379
380pub(super) fn heuristic_requires_live_refresh(heuristic: ConstructionHeuristicType) -> bool {
381    matches!(
382        heuristic,
383        ConstructionHeuristicType::FirstFitDecreasing
384            | ConstructionHeuristicType::WeakestFit
385            | ConstructionHeuristicType::WeakestFitDecreasing
386            | ConstructionHeuristicType::StrongestFit
387            | ConstructionHeuristicType::StrongestFitDecreasing
388            | ConstructionHeuristicType::AllocateEntityFromQueue
389            | ConstructionHeuristicType::AllocateToValueFromQueue
390    )
391}