Skip to main content

solverforge_solver/descriptor_standard/
construction.rs

1use std::any::Any;
2use std::fmt::{self, Debug};
3use std::marker::PhantomData;
4
5use solverforge_config::{ConstructionHeuristicConfig, ConstructionHeuristicType};
6use solverforge_core::domain::{PlanningSolution, SolutionDescriptor};
7use solverforge_scoring::Director;
8
9use crate::heuristic::selector::EntityReference;
10use crate::phase::construction::{
11    BestFitForager, ConstructionHeuristicPhase, EntityPlacer, FirstFitForager, Placement,
12};
13use crate::scope::{ProgressCallback, SolverScope};
14
15use super::bindings::{collect_bindings, find_binding, VariableBinding};
16use super::move_types::{DescriptorChangeMove, DescriptorEitherMove};
17
18pub enum DescriptorConstruction<S: PlanningSolution> {
19    FirstFit(
20        ConstructionHeuristicPhase<
21            S,
22            DescriptorEitherMove<S>,
23            DescriptorEntityPlacer<S>,
24            FirstFitForager<S, DescriptorEitherMove<S>>,
25        >,
26    ),
27    BestFit(
28        ConstructionHeuristicPhase<
29            S,
30            DescriptorEitherMove<S>,
31            DescriptorEntityPlacer<S>,
32            BestFitForager<S, DescriptorEitherMove<S>>,
33        >,
34    ),
35}
36
37impl<S: PlanningSolution> Debug for DescriptorConstruction<S> {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        match self {
40            Self::FirstFit(phase) => write!(f, "DescriptorConstruction::FirstFit({phase:?})"),
41            Self::BestFit(phase) => write!(f, "DescriptorConstruction::BestFit({phase:?})"),
42        }
43    }
44}
45
46impl<S, D, ProgressCb> crate::phase::Phase<S, D, ProgressCb> for DescriptorConstruction<S>
47where
48    S: PlanningSolution + 'static,
49    D: Director<S>,
50    ProgressCb: ProgressCallback<S>,
51{
52    fn solve(&mut self, solver_scope: &mut SolverScope<'_, S, D, ProgressCb>) {
53        match self {
54            Self::FirstFit(phase) => phase.solve(solver_scope),
55            Self::BestFit(phase) => phase.solve(solver_scope),
56        }
57    }
58
59    fn phase_type_name(&self) -> &'static str {
60        "DescriptorConstruction"
61    }
62}
63
64#[derive(Clone)]
65pub struct DescriptorEntityPlacer<S> {
66    bindings: Vec<VariableBinding>,
67    solution_descriptor: SolutionDescriptor,
68    _phantom: PhantomData<fn() -> S>,
69}
70
71impl<S> Debug for DescriptorEntityPlacer<S> {
72    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73        f.debug_struct("DescriptorEntityPlacer")
74            .field("bindings", &self.bindings)
75            .finish()
76    }
77}
78
79impl<S> DescriptorEntityPlacer<S> {
80    fn new(bindings: Vec<VariableBinding>, solution_descriptor: SolutionDescriptor) -> Self {
81        Self {
82            bindings,
83            solution_descriptor,
84            _phantom: PhantomData,
85        }
86    }
87}
88
89impl<S> EntityPlacer<S, DescriptorEitherMove<S>> for DescriptorEntityPlacer<S>
90where
91    S: PlanningSolution + 'static,
92{
93    fn get_placements<D: Director<S>>(
94        &self,
95        score_director: &D,
96    ) -> Vec<Placement<S, DescriptorEitherMove<S>>> {
97        let mut placements = Vec::new();
98
99        for binding in &self.bindings {
100            let count = score_director
101                .entity_count(binding.descriptor_index)
102                .unwrap_or(0);
103
104            for entity_index in 0..count {
105                let entity = self
106                    .solution_descriptor
107                    .get_entity(
108                        score_director.working_solution() as &dyn Any,
109                        binding.descriptor_index,
110                        entity_index,
111                    )
112                    .expect("entity lookup failed for descriptor construction");
113                let current_value = (binding.getter)(entity);
114                if current_value.is_some() {
115                    continue;
116                }
117
118                let moves = binding
119                    .values_for_entity(
120                        &self.solution_descriptor,
121                        score_director.working_solution() as &dyn Any,
122                        entity,
123                    )
124                    .into_iter()
125                    .map(|value| {
126                        DescriptorEitherMove::Change(DescriptorChangeMove::new(
127                            binding.clone(),
128                            entity_index,
129                            Some(value),
130                            self.solution_descriptor.clone(),
131                        ))
132                    })
133                    .collect::<Vec<_>>();
134
135                if moves.is_empty() {
136                    continue;
137                }
138
139                placements.push(Placement::new(
140                    EntityReference::new(binding.descriptor_index, entity_index),
141                    moves,
142                ));
143            }
144        }
145
146        placements
147    }
148}
149
150pub fn build_descriptor_construction<S>(
151    config: Option<&ConstructionHeuristicConfig>,
152    descriptor: &SolutionDescriptor,
153) -> DescriptorConstruction<S>
154where
155    S: PlanningSolution + 'static,
156{
157    let bindings = config
158        .map(|cfg| {
159            let matched = find_binding(
160                &collect_bindings(descriptor),
161                cfg.target.entity_class.as_deref(),
162                cfg.target.variable_name.as_deref(),
163            );
164            assert!(
165                !matched.is_empty(),
166                "construction heuristic matched no standard planning variables for entity_class={:?} variable_name={:?}",
167                cfg.target.entity_class,
168                cfg.target.variable_name
169            );
170            matched
171        })
172        .unwrap_or_else(|| collect_bindings(descriptor));
173    let placer = DescriptorEntityPlacer::new(bindings, descriptor.clone());
174    let construction_type = config
175        .map(|cfg| cfg.construction_heuristic_type)
176        .unwrap_or(ConstructionHeuristicType::FirstFit);
177
178    match construction_type {
179        ConstructionHeuristicType::FirstFit => DescriptorConstruction::FirstFit(
180            ConstructionHeuristicPhase::new(placer, FirstFitForager::new()),
181        ),
182        ConstructionHeuristicType::CheapestInsertion => DescriptorConstruction::BestFit(
183            ConstructionHeuristicPhase::new(placer, BestFitForager::new()),
184        ),
185        ConstructionHeuristicType::FirstFitDecreasing
186        | ConstructionHeuristicType::WeakestFit
187        | ConstructionHeuristicType::WeakestFitDecreasing
188        | ConstructionHeuristicType::StrongestFit
189        | ConstructionHeuristicType::StrongestFitDecreasing
190        | ConstructionHeuristicType::AllocateEntityFromQueue
191        | ConstructionHeuristicType::AllocateToValueFromQueue
192        | ConstructionHeuristicType::ListRoundRobin
193        | ConstructionHeuristicType::ListCheapestInsertion
194        | ConstructionHeuristicType::ListRegretInsertion
195        | ConstructionHeuristicType::ListClarkeWright
196        | ConstructionHeuristicType::ListKOpt => {
197            panic!(
198                "descriptor standard construction does not support {:?}",
199                construction_type
200            );
201        }
202    }
203}