solverforge_solver/descriptor_standard/
construction.rs1use 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}