solverforge_solver/phase/localsearch/forager/
improving.rs1use std::fmt::Debug;
2use std::marker::PhantomData;
3
4use solverforge_core::domain::PlanningSolution;
5use solverforge_core::score::Score;
6
7use crate::heuristic::r#move::Move;
8
9use super::LocalSearchForager;
10
11pub struct FirstBestScoreImprovingForager<S>
17where
18 S: PlanningSolution,
19{
20 best_score: S::Score,
21 accepted_moves: Vec<(usize, S::Score)>,
22 found_best_improving: bool,
23 _phantom: PhantomData<fn() -> S>,
24}
25
26impl<S> FirstBestScoreImprovingForager<S>
27where
28 S: PlanningSolution,
29{
30 pub fn new() -> Self {
31 Self {
32 best_score: S::Score::zero(),
33 accepted_moves: Vec::new(),
34 found_best_improving: false,
35 _phantom: PhantomData,
36 }
37 }
38}
39
40impl<S> Default for FirstBestScoreImprovingForager<S>
41where
42 S: PlanningSolution,
43{
44 fn default() -> Self {
45 Self::new()
46 }
47}
48
49impl<S> Debug for FirstBestScoreImprovingForager<S>
50where
51 S: PlanningSolution,
52{
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 f.debug_struct("FirstBestScoreImprovingForager")
55 .field("found_best_improving", &self.found_best_improving)
56 .finish()
57 }
58}
59
60impl<S> Clone for FirstBestScoreImprovingForager<S>
61where
62 S: PlanningSolution,
63{
64 fn clone(&self) -> Self {
65 Self {
66 best_score: self.best_score,
67 accepted_moves: Vec::new(),
68 found_best_improving: false,
69 _phantom: PhantomData,
70 }
71 }
72}
73
74impl<S, M> LocalSearchForager<S, M> for FirstBestScoreImprovingForager<S>
75where
76 S: PlanningSolution,
77 M: Move<S>,
78{
79 fn step_started(&mut self, best_score: S::Score, _last_step_score: S::Score) {
80 self.best_score = best_score;
81 self.accepted_moves.clear();
82 self.found_best_improving = false;
83 }
84
85 fn add_move_index(&mut self, index: usize, score: S::Score) {
86 if score > self.best_score {
87 self.accepted_moves.clear();
88 self.accepted_moves.push((index, score));
89 self.found_best_improving = true;
90 } else if !self.found_best_improving {
91 self.accepted_moves.push((index, score));
92 }
93 }
94
95 fn is_quit_early(&self) -> bool {
96 self.found_best_improving
97 }
98
99 fn pick_move_index(&mut self) -> Option<(usize, S::Score)> {
100 if self.accepted_moves.is_empty() {
101 return None;
102 }
103 if self.found_best_improving {
104 return self.accepted_moves.pop();
105 }
106
107 let mut best_idx = 0;
108 let mut best_score = self.accepted_moves[0].1;
109 for (i, &(_, score)) in self.accepted_moves.iter().enumerate().skip(1) {
110 if score > best_score {
111 best_idx = i;
112 best_score = score;
113 }
114 }
115 Some(self.accepted_moves.swap_remove(best_idx))
116 }
117}
118
119pub struct FirstLastStepScoreImprovingForager<S>
125where
126 S: PlanningSolution,
127{
128 last_step_score: S::Score,
129 accepted_moves: Vec<(usize, S::Score)>,
130 found_last_step_improving: bool,
131 _phantom: PhantomData<fn() -> S>,
132}
133
134impl<S> FirstLastStepScoreImprovingForager<S>
135where
136 S: PlanningSolution,
137{
138 pub fn new() -> Self {
139 Self {
140 last_step_score: S::Score::zero(),
141 accepted_moves: Vec::new(),
142 found_last_step_improving: false,
143 _phantom: PhantomData,
144 }
145 }
146}
147
148impl<S> Default for FirstLastStepScoreImprovingForager<S>
149where
150 S: PlanningSolution,
151{
152 fn default() -> Self {
153 Self::new()
154 }
155}
156
157impl<S> Debug for FirstLastStepScoreImprovingForager<S>
158where
159 S: PlanningSolution,
160{
161 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162 f.debug_struct("FirstLastStepScoreImprovingForager")
163 .field("found_last_step_improving", &self.found_last_step_improving)
164 .finish()
165 }
166}
167
168impl<S> Clone for FirstLastStepScoreImprovingForager<S>
169where
170 S: PlanningSolution,
171{
172 fn clone(&self) -> Self {
173 Self {
174 last_step_score: self.last_step_score,
175 accepted_moves: Vec::new(),
176 found_last_step_improving: false,
177 _phantom: PhantomData,
178 }
179 }
180}
181
182impl<S, M> LocalSearchForager<S, M> for FirstLastStepScoreImprovingForager<S>
183where
184 S: PlanningSolution,
185 M: Move<S>,
186{
187 fn step_started(&mut self, _best_score: S::Score, last_step_score: S::Score) {
188 self.last_step_score = last_step_score;
189 self.accepted_moves.clear();
190 self.found_last_step_improving = false;
191 }
192
193 fn add_move_index(&mut self, index: usize, score: S::Score) {
194 if score > self.last_step_score {
195 self.accepted_moves.clear();
196 self.accepted_moves.push((index, score));
197 self.found_last_step_improving = true;
198 } else if !self.found_last_step_improving {
199 self.accepted_moves.push((index, score));
200 }
201 }
202
203 fn is_quit_early(&self) -> bool {
204 self.found_last_step_improving
205 }
206
207 fn pick_move_index(&mut self) -> Option<(usize, S::Score)> {
208 if self.accepted_moves.is_empty() {
209 return None;
210 }
211 if self.found_last_step_improving {
212 return self.accepted_moves.pop();
213 }
214
215 let mut best_idx = 0;
216 let mut best_score = self.accepted_moves[0].1;
217 for (i, &(_, score)) in self.accepted_moves.iter().enumerate().skip(1) {
218 if score > best_score {
219 best_idx = i;
220 best_score = score;
221 }
222 }
223 Some(self.accepted_moves.swap_remove(best_idx))
224 }
225}