solverforge_solver/scope/solver/
scope_core.rs1pub struct SolverScope<'t, S: PlanningSolution, D: Director<S>, ProgressCb = ()> {
2 score_director: D,
3 best_solution: Option<S>,
4 current_score: Option<S::Score>,
5 best_score: Option<S::Score>,
6 rng: StdRng,
7 start_time: Option<Instant>,
8 paused_at: Option<Instant>,
9 total_step_count: u64,
10 terminate: Option<&'t AtomicBool>,
11 runtime: Option<SolverRuntime<S>>,
12 stats: SolverStats,
13 time_limit: Option<Duration>,
14 progress_callback: ProgressCb,
15 terminal_reason: Option<SolverTerminalReason>,
16 last_best_elapsed: Option<Duration>,
17 best_solution_revision: Option<u64>,
18 solution_revision: u64,
19 construction_frontier: ConstructionFrontier,
20 pub inphase_step_count_limit: Option<u64>,
21 pub inphase_move_count_limit: Option<u64>,
22 pub inphase_score_calc_count_limit: Option<u64>,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub(crate) enum PendingControl {
27 Continue,
28 PauseRequested,
29 CancelRequested,
30 ConfigTerminationRequested,
31}
32
33impl<'t, S: PlanningSolution, D: Director<S>> SolverScope<'t, S, D, ()> {
34 pub fn new(score_director: D) -> Self {
35 let construction_frontier = ConstructionFrontier::new();
36 Self {
37 score_director,
38 best_solution: None,
39 current_score: None,
40 best_score: None,
41 rng: StdRng::from_rng(&mut rand::rng()),
42 start_time: None,
43 paused_at: None,
44 total_step_count: 0,
45 terminate: None,
46 runtime: None,
47 stats: SolverStats::default(),
48 time_limit: None,
49 progress_callback: (),
50 terminal_reason: None,
51 last_best_elapsed: None,
52 best_solution_revision: None,
53 solution_revision: 1,
54 construction_frontier,
55 inphase_step_count_limit: None,
56 inphase_move_count_limit: None,
57 inphase_score_calc_count_limit: None,
58 }
59 }
60}
61
62impl<'t, S: PlanningSolution, D: Director<S>, ProgressCb: ProgressCallback<S>>
63 SolverScope<'t, S, D, ProgressCb>
64{
65 pub fn new_with_callback(
66 score_director: D,
67 callback: ProgressCb,
68 terminate: Option<&'t AtomicBool>,
69 runtime: Option<SolverRuntime<S>>,
70 ) -> Self {
71 let construction_frontier = ConstructionFrontier::new();
72 Self {
73 score_director,
74 best_solution: None,
75 current_score: None,
76 best_score: None,
77 rng: StdRng::from_rng(&mut rand::rng()),
78 start_time: None,
79 paused_at: None,
80 total_step_count: 0,
81 terminate,
82 runtime,
83 stats: SolverStats::default(),
84 time_limit: None,
85 progress_callback: callback,
86 terminal_reason: None,
87 last_best_elapsed: None,
88 best_solution_revision: None,
89 solution_revision: 1,
90 construction_frontier,
91 inphase_step_count_limit: None,
92 inphase_move_count_limit: None,
93 inphase_score_calc_count_limit: None,
94 }
95 }
96
97 pub fn with_terminate(mut self, terminate: Option<&'t AtomicBool>) -> Self {
98 self.terminate = terminate;
99 self
100 }
101
102 pub fn with_runtime(mut self, runtime: Option<SolverRuntime<S>>) -> Self {
103 self.runtime = runtime;
104 self
105 }
106
107 pub fn with_seed(mut self, seed: u64) -> Self {
108 self.rng = StdRng::seed_from_u64(seed);
109 self
110 }
111
112 pub fn with_progress_callback<F: ProgressCallback<S>>(
113 self,
114 callback: F,
115 ) -> SolverScope<'t, S, D, F> {
116 SolverScope {
117 score_director: self.score_director,
118 best_solution: self.best_solution,
119 current_score: self.current_score,
120 best_score: self.best_score,
121 rng: self.rng,
122 start_time: self.start_time,
123 paused_at: self.paused_at,
124 total_step_count: self.total_step_count,
125 terminate: self.terminate,
126 runtime: self.runtime,
127 stats: self.stats,
128 time_limit: self.time_limit,
129 progress_callback: callback,
130 terminal_reason: self.terminal_reason,
131 last_best_elapsed: self.last_best_elapsed,
132 best_solution_revision: self.best_solution_revision,
133 solution_revision: self.solution_revision,
134 construction_frontier: self.construction_frontier,
135 inphase_step_count_limit: self.inphase_step_count_limit,
136 inphase_move_count_limit: self.inphase_move_count_limit,
137 inphase_score_calc_count_limit: self.inphase_score_calc_count_limit,
138 }
139 }
140
141 pub fn start_solving(&mut self) {
142 self.start_time = Some(Instant::now());
143 self.paused_at = None;
144 self.total_step_count = 0;
145 self.terminal_reason = None;
146 self.last_best_elapsed = None;
147 self.best_solution_revision = None;
148 self.solution_revision = 1;
149 self.construction_frontier.reset();
150 self.stats.start();
151 }
152
153 pub fn elapsed(&self) -> Option<Duration> {
154 match (self.start_time, self.paused_at) {
155 (Some(start), Some(paused_at)) => Some(paused_at.duration_since(start)),
156 (Some(start), None) => Some(start.elapsed()),
157 _ => None,
158 }
159 }
160
161 pub fn time_since_last_improvement(&self) -> Option<Duration> {
162 let elapsed = self.elapsed()?;
163 let last_best_elapsed = self.last_best_elapsed?;
164 Some(elapsed.saturating_sub(last_best_elapsed))
165 }
166
167 pub fn score_director(&self) -> &D {
168 &self.score_director
169 }
170
171 pub(crate) fn score_director_mut(&mut self) -> &mut D {
172 &mut self.score_director
173 }
174
175 pub fn working_solution(&self) -> &S {
176 self.score_director.working_solution()
177 }
178
179 pub fn trial<T, F>(&mut self, trial: F) -> T
180 where
181 F: FnOnce(&mut RecordingDirector<'_, S, D>) -> T,
182 {
183 let mut recording = RecordingDirector::new(&mut self.score_director);
184 let output = trial(&mut recording);
185 recording.undo_changes();
186 output
187 }
188
189 pub fn mutate<T, F>(&mut self, mutate: F) -> T
190 where
191 F: FnOnce(&mut D) -> T,
192 {
193 self.committed_mutation(mutate)
194 }
195
196 pub fn calculate_score(&mut self) -> S::Score {
197 self.stats.record_score_calculation();
198 let score = self.score_director.calculate_score();
199 self.current_score = Some(score);
200 score
201 }
202
203 pub fn initialize_working_solution_as_best(&mut self) -> S::Score {
204 if self.start_time.is_none() {
205 self.start_solving();
206 }
207 let score = self.calculate_score();
208 let solution = self.score_director.clone_working_solution();
209 self.set_best_solution(solution, score);
210 score
211 }
212
213 pub fn replace_working_solution_and_reinitialize(&mut self, solution: S) -> S::Score {
214 *self.score_director.working_solution_mut() = solution;
215 self.score_director.reset();
216 self.current_score = None;
217 self.best_solution_revision = None;
218 self.solution_revision = 1;
219 self.construction_frontier.reset();
220 self.calculate_score()
221 }
222
223 pub fn best_solution(&self) -> Option<&S> {
224 self.best_solution.as_ref()
225 }
226
227 pub fn best_score(&self) -> Option<&S::Score> {
228 self.best_score.as_ref()
229 }
230
231 pub fn current_score(&self) -> Option<&S::Score> {
232 self.current_score.as_ref()
233 }
234
235 pub(crate) fn is_scalar_slot_completed(&self, slot_id: ConstructionSlotId) -> bool {
236 self.construction_frontier
237 .is_scalar_completed(slot_id, self.solution_revision)
238 }
239
240 pub(crate) fn mark_scalar_slot_completed(&mut self, slot_id: ConstructionSlotId) {
241 self.construction_frontier
242 .mark_scalar_completed(slot_id, self.solution_revision);
243 }
244
245 pub(crate) fn is_group_slot_completed(&self, slot_id: ConstructionGroupSlotId) -> bool {
246 self.construction_frontier
247 .is_group_completed(slot_id, self.solution_revision)
248 }
249
250 pub(crate) fn mark_group_slot_completed(&mut self, slot_id: ConstructionGroupSlotId) {
251 self.construction_frontier
252 .mark_group_completed(slot_id, self.solution_revision);
253 }
254
255 pub(crate) fn is_list_element_completed(&self, element_id: ConstructionListElementId) -> bool {
256 self.construction_frontier
257 .is_list_completed(element_id, self.solution_revision)
258 }
259
260 pub(crate) fn mark_list_element_completed(&mut self, element_id: ConstructionListElementId) {
261 self.construction_frontier
262 .mark_list_completed(element_id, self.solution_revision);
263 }
264
265 pub(crate) fn solution_revision(&self) -> u64 {
266 self.solution_revision
267 }
268
269 pub(crate) fn apply_committed_move<M>(&mut self, mov: &M)
270 where
271 M: Move<S>,
272 {
273 self.committed_mutation(|score_director| mov.do_move(score_director));
274 }
275
276 pub(crate) fn apply_committed_change<F>(&mut self, change: F)
277 where
278 F: FnOnce(&mut D),
279 {
280 self.committed_mutation(change);
281 }
282
283 pub(crate) fn construction_frontier(&self) -> &ConstructionFrontier {
284 &self.construction_frontier
285 }
286}