solverforge_solver/phase/construction/
mod.rs1mod forager;
7mod placer;
8
9use std::fmt::Debug;
10use std::marker::PhantomData;
11
12use solverforge_core::domain::PlanningSolution;
13
14use crate::heuristic::r#move::Move;
15use crate::phase::Phase;
16use crate::scope::{PhaseScope, SolverScope, StepScope};
17
18pub use forager::{
19 BestFitForager, ConstructionForager, FirstFeasibleForager, FirstFitForager,
20 StrongestFitForager, WeakestFitForager,
21};
22pub use placer::{EntityPlacer, Placement, QueuedEntityPlacer, SortedEntityPlacer};
23
24#[derive(Debug, Clone)]
26pub struct ConstructionHeuristicConfig {
27 pub forager_type: ForagerType,
29}
30
31impl Default for ConstructionHeuristicConfig {
32 fn default() -> Self {
33 Self {
34 forager_type: ForagerType::FirstFit,
35 }
36 }
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum ForagerType {
42 FirstFit,
44 BestFit,
46}
47
48pub struct ConstructionHeuristicPhase<S, M>
57where
58 S: PlanningSolution,
59 M: Move<S>,
60{
61 placer: Box<dyn EntityPlacer<S, M>>,
63 forager: Box<dyn ConstructionForager<S, M>>,
65 _phantom: PhantomData<M>,
66}
67
68impl<S, M> ConstructionHeuristicPhase<S, M>
69where
70 S: PlanningSolution,
71 M: Move<S>,
72{
73 pub fn new(
75 placer: Box<dyn EntityPlacer<S, M>>,
76 forager: Box<dyn ConstructionForager<S, M>>,
77 ) -> Self {
78 Self {
79 placer,
80 forager,
81 _phantom: PhantomData,
82 }
83 }
84}
85
86impl<S, M> Debug for ConstructionHeuristicPhase<S, M>
87where
88 S: PlanningSolution,
89 M: Move<S>,
90{
91 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92 f.debug_struct("ConstructionHeuristicPhase")
93 .field("placer", &self.placer)
94 .field("forager", &self.forager)
95 .finish()
96 }
97}
98
99impl<S, M> Phase<S> for ConstructionHeuristicPhase<S, M>
100where
101 S: PlanningSolution,
102 M: Move<S>,
103{
104 fn solve(&mut self, solver_scope: &mut SolverScope<S>) {
105 let mut phase_scope = PhaseScope::new(solver_scope, 0);
106
107 let placements = self.placer.get_placements(phase_scope.score_director());
109
110 for placement in placements {
111 if phase_scope.solver_scope().is_terminate_early() {
113 break;
114 }
115
116 let mut step_scope = StepScope::new(&mut phase_scope);
117
118 let selected_move = self
120 .forager
121 .pick_move(&placement, step_scope.score_director_mut());
122
123 if let Some(m) = selected_move {
124 m.do_move(step_scope.score_director_mut());
126
127 let step_score = step_scope.calculate_score();
129 step_scope.set_step_score(step_score);
130 }
131
132 step_scope.complete();
133 }
134
135 phase_scope.update_best_solution();
137 }
138
139 fn phase_type_name(&self) -> &'static str {
140 "ConstructionHeuristic"
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147 use crate::heuristic::r#move::ChangeMove;
148 use crate::heuristic::selector::{FromSolutionEntitySelector, StaticTypedValueSelector};
149 use crate::manager::{ConstructionPhaseFactory, SolverPhaseFactory};
150 use solverforge_core::domain::{EntityDescriptor, SolutionDescriptor, TypedEntityExtractor};
151 use solverforge_core::score::SimpleScore;
152 use solverforge_scoring::SimpleScoreDirector;
153 use std::any::TypeId;
154
155 #[derive(Clone, Debug)]
156 struct Queen {
157 column: i32,
158 row: Option<i32>,
159 }
160
161 #[derive(Clone, Debug)]
162 struct NQueensSolution {
163 n: i32,
164 queens: Vec<Queen>,
165 score: Option<SimpleScore>,
166 }
167
168 impl PlanningSolution for NQueensSolution {
169 type Score = SimpleScore;
170
171 fn score(&self) -> Option<Self::Score> {
172 self.score
173 }
174
175 fn set_score(&mut self, score: Option<Self::Score>) {
176 self.score = score;
177 }
178 }
179
180 fn get_queens(s: &NQueensSolution) -> &Vec<Queen> {
181 &s.queens
182 }
183
184 fn get_queens_mut(s: &mut NQueensSolution) -> &mut Vec<Queen> {
185 &mut s.queens
186 }
187
188 fn get_queen_row(s: &NQueensSolution, idx: usize) -> Option<i32> {
190 s.queens.get(idx).and_then(|q| q.row)
191 }
192
193 fn set_queen_row(s: &mut NQueensSolution, idx: usize, v: Option<i32>) {
195 if let Some(queen) = s.queens.get_mut(idx) {
196 queen.row = v;
197 }
198 }
199
200 fn calculate_conflicts(solution: &NQueensSolution) -> SimpleScore {
201 let mut conflicts = 0i64;
202
203 for (i, q1) in solution.queens.iter().enumerate() {
204 if let Some(row1) = q1.row {
205 for (_, q2) in solution.queens.iter().enumerate().skip(i + 1) {
206 if let Some(row2) = q2.row {
207 if row1 == row2 {
209 conflicts += 1;
210 }
211 let col_diff = (q2.column - q1.column).abs();
213 let row_diff = (row2 - row1).abs();
214 if col_diff == row_diff {
215 conflicts += 1;
216 }
217 }
218 }
219 }
220 }
221
222 SimpleScore::of(-conflicts)
223 }
224
225 fn create_test_director(
226 n: i32,
227 ) -> SimpleScoreDirector<NQueensSolution, impl Fn(&NQueensSolution) -> SimpleScore> {
228 let queens: Vec<_> = (0..n)
229 .map(|col| Queen {
230 column: col,
231 row: None,
232 })
233 .collect();
234
235 let solution = NQueensSolution {
236 n,
237 queens,
238 score: None,
239 };
240
241 let extractor = Box::new(TypedEntityExtractor::new(
242 "Queen",
243 "queens",
244 get_queens,
245 get_queens_mut,
246 ));
247 let entity_desc = EntityDescriptor::new("Queen", TypeId::of::<Queen>(), "queens")
248 .with_extractor(extractor);
249
250 let descriptor =
251 SolutionDescriptor::new("NQueensSolution", TypeId::of::<NQueensSolution>())
252 .with_entity(entity_desc);
253
254 SimpleScoreDirector::with_calculator(solution, descriptor, calculate_conflicts)
255 }
256
257 type NQueensMove = ChangeMove<NQueensSolution, i32>;
258
259 fn create_placer(values: Vec<i32>) -> Box<dyn EntityPlacer<NQueensSolution, NQueensMove>> {
260 let es = Box::new(FromSolutionEntitySelector::new(0));
261 let vs = Box::new(StaticTypedValueSelector::new(values));
262 Box::new(QueuedEntityPlacer::new(
263 es,
264 vs,
265 get_queen_row,
266 set_queen_row,
267 0,
268 "row",
269 ))
270 }
271
272 #[test]
273 fn test_construction_first_fit() {
274 let director = create_test_director(4);
275 let mut solver_scope = SolverScope::new(Box::new(director));
276
277 let values: Vec<i32> = (0..4).collect();
278 let factory =
279 ConstructionPhaseFactory::<NQueensSolution, NQueensMove, _>::first_fit(move || {
280 create_placer(values.clone())
281 });
282 let mut phase = factory.create_phase();
283
284 phase.solve(&mut solver_scope);
285
286 let solution = solver_scope.working_solution();
288 assert_eq!(solution.n, 4);
289 for queen in &solution.queens {
290 assert!(queen.row.is_some(), "Queen should have a row assigned");
291 }
292
293 assert!(solver_scope.best_solution().is_some());
295 }
296
297 #[test]
298 fn test_construction_best_fit() {
299 let director = create_test_director(4);
300 let mut solver_scope = SolverScope::new(Box::new(director));
301
302 let values: Vec<i32> = (0..4).collect();
303 let factory =
304 ConstructionPhaseFactory::<NQueensSolution, NQueensMove, _>::best_fit(move || {
305 create_placer(values.clone())
306 });
307 let mut phase = factory.create_phase();
308
309 phase.solve(&mut solver_scope);
310
311 let solution = solver_scope.working_solution();
313 for queen in &solution.queens {
314 assert!(queen.row.is_some(), "Queen should have a row assigned");
315 }
316
317 assert!(solver_scope.best_solution().is_some());
319 assert!(solver_scope.best_score().is_some());
320 }
321
322 #[test]
323 fn test_construction_empty_solution() {
324 let director = create_test_director(0);
325 let mut solver_scope = SolverScope::new(Box::new(director));
326
327 let values: Vec<i32> = vec![];
328 let factory =
329 ConstructionPhaseFactory::<NQueensSolution, NQueensMove, _>::first_fit(move || {
330 create_placer(values.clone())
331 });
332 let mut phase = factory.create_phase();
333
334 phase.solve(&mut solver_scope);
336 }
337}