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