Skip to main content

solverforge_solver/scope/
phase.rs

1// Phase-level scope.
2
3use std::time::Duration;
4use std::time::Instant;
5
6use solverforge_core::domain::PlanningSolution;
7use solverforge_scoring::Director;
8
9use super::solver::ProgressCallback;
10use super::SolverScope;
11use crate::stats::PhaseStats;
12
13/// Scope for a single phase of solving.
14///
15/// # Type Parameters
16/// * `'t` - Lifetime of the termination flag
17/// * `'a` - Lifetime of the solver scope reference
18/// * `S` - The planning solution type
19/// * `D` - The score director type
20/// * `BestCb` - The best-solution callback type
21pub struct PhaseScope<'t, 'a, S: PlanningSolution, D: Director<S>, BestCb = ()> {
22    // Reference to the parent solver scope.
23    solver_scope: &'a mut SolverScope<'t, S, D, BestCb>,
24    // Index of this phase (0-based).
25    phase_index: usize,
26    // Score at the start of this phase.
27    starting_score: Option<S::Score>,
28    // Number of steps in this phase.
29    step_count: u64,
30    // When this phase started.
31    start_time: Instant,
32    // Phase statistics.
33    stats: PhaseStats,
34}
35
36impl<'t, 'a, S: PlanningSolution, D: Director<S>, BestCb: ProgressCallback<S>>
37    PhaseScope<'t, 'a, S, D, BestCb>
38{
39    pub fn new(solver_scope: &'a mut SolverScope<'t, S, D, BestCb>, phase_index: usize) -> Self {
40        let starting_score = solver_scope.best_score().cloned();
41        Self {
42            solver_scope,
43            phase_index,
44            starting_score,
45            step_count: 0,
46            start_time: Instant::now(),
47            stats: PhaseStats::new(phase_index, "Unknown"),
48        }
49    }
50
51    pub fn with_phase_type(
52        solver_scope: &'a mut SolverScope<'t, S, D, BestCb>,
53        phase_index: usize,
54        phase_type: &'static str,
55    ) -> Self {
56        let starting_score = solver_scope.best_score().cloned();
57        Self {
58            solver_scope,
59            phase_index,
60            starting_score,
61            step_count: 0,
62            start_time: Instant::now(),
63            stats: PhaseStats::new(phase_index, phase_type),
64        }
65    }
66
67    pub fn phase_index(&self) -> usize {
68        self.phase_index
69    }
70
71    pub fn starting_score(&self) -> Option<&S::Score> {
72        self.starting_score.as_ref()
73    }
74
75    pub fn elapsed(&self) -> std::time::Duration {
76        self.start_time.elapsed()
77    }
78
79    pub fn step_count(&self) -> u64 {
80        self.step_count
81    }
82
83    /// Increments the phase step count.
84    pub fn increment_step_count(&mut self) -> u64 {
85        self.step_count += 1;
86        self.stats.record_step();
87        self.solver_scope.increment_step_count();
88        self.step_count
89    }
90
91    pub fn solver_scope(&self) -> &SolverScope<'t, S, D, BestCb> {
92        self.solver_scope
93    }
94
95    pub fn solver_scope_mut(&mut self) -> &mut SolverScope<'t, S, D, BestCb> {
96        self.solver_scope
97    }
98
99    pub fn score_director(&self) -> &D {
100        self.solver_scope.score_director()
101    }
102
103    pub(crate) fn score_director_mut(&mut self) -> &mut D {
104        self.solver_scope.score_director_mut()
105    }
106
107    pub fn mutate<T, F>(&mut self, mutate: F) -> T
108    where
109        F: FnOnce(&mut D) -> T,
110    {
111        self.solver_scope.mutate(mutate)
112    }
113
114    /// Calculates the current score.
115    pub fn calculate_score(&mut self) -> S::Score {
116        self.stats.record_score_calculation();
117        self.solver_scope.calculate_score()
118    }
119
120    /// Updates best solution.
121    pub fn update_best_solution(&mut self) {
122        self.solver_scope.update_best_solution()
123    }
124
125    /// Publishes the current working solution when it ties the current best score.
126    pub(crate) fn promote_current_solution_on_score_tie(&mut self) {
127        self.solver_scope.promote_current_solution_on_score_tie()
128    }
129
130    pub fn stats(&self) -> &PhaseStats {
131        &self.stats
132    }
133
134    pub fn stats_mut(&mut self) -> &mut PhaseStats {
135        &mut self.stats
136    }
137
138    pub fn record_generated_batch(&mut self, count: u64, duration: Duration) {
139        self.stats.record_generated_batch(count, duration);
140        self.solver_scope
141            .stats_mut()
142            .record_generated_batch(count, duration);
143    }
144
145    pub fn record_generated_move(&mut self, duration: Duration) {
146        self.record_generated_batch(1, duration);
147    }
148
149    pub fn record_selector_generated_move(&mut self, selector_index: usize, duration: Duration) {
150        self.stats
151            .record_selector_generated(selector_index, 1, duration);
152        self.solver_scope
153            .stats_mut()
154            .record_selector_generated(selector_index, 1, duration);
155    }
156
157    pub fn record_selector_generated_move_with_label(
158        &mut self,
159        selector_index: usize,
160        selector_label: impl Into<String>,
161        duration: Duration,
162    ) {
163        let selector_label = selector_label.into();
164        self.stats.record_selector_generated_with_label(
165            selector_index,
166            selector_label.clone(),
167            1,
168            duration,
169        );
170        self.solver_scope
171            .stats_mut()
172            .record_selector_generated_with_label(selector_index, selector_label, 1, duration);
173    }
174
175    pub fn record_generation_time(&mut self, duration: Duration) {
176        self.stats.record_generation_time(duration);
177        self.solver_scope
178            .stats_mut()
179            .record_generation_time(duration);
180    }
181
182    pub fn record_evaluated_move(&mut self, duration: Duration) {
183        self.stats.record_evaluated_move(duration);
184        self.solver_scope.record_evaluated_move(duration);
185    }
186
187    pub fn record_selector_evaluated_move(&mut self, selector_index: usize, duration: Duration) {
188        self.stats
189            .record_selector_evaluated(selector_index, duration);
190        self.solver_scope
191            .record_selector_evaluated(selector_index, duration);
192    }
193
194    pub fn record_move_accepted(&mut self) {
195        self.stats.record_move_accepted();
196        self.solver_scope.stats_mut().record_move_accepted();
197    }
198
199    pub fn record_move_applied(&mut self) {
200        self.stats.record_move_applied();
201        self.solver_scope.stats_mut().record_move_applied();
202    }
203
204    pub fn record_selector_move_accepted(&mut self, selector_index: usize) {
205        self.stats.record_selector_accepted(selector_index);
206        self.solver_scope
207            .stats_mut()
208            .record_selector_accepted(selector_index);
209    }
210
211    pub fn record_selector_move_applied(&mut self, selector_index: usize) {
212        self.stats.record_selector_applied(selector_index);
213        self.solver_scope
214            .stats_mut()
215            .record_selector_applied(selector_index);
216    }
217
218    pub fn record_move_not_doable(&mut self) {
219        self.stats.record_move_not_doable();
220        self.solver_scope.stats_mut().record_move_not_doable();
221    }
222
223    pub fn record_selector_move_not_doable(&mut self, selector_index: usize) {
224        self.stats.record_selector_not_doable(selector_index);
225        self.solver_scope
226            .stats_mut()
227            .record_selector_not_doable(selector_index);
228    }
229
230    pub fn record_move_acceptor_rejected(&mut self) {
231        self.stats.record_move_acceptor_rejected();
232        self.solver_scope
233            .stats_mut()
234            .record_move_acceptor_rejected();
235    }
236
237    pub fn record_selector_move_acceptor_rejected(&mut self, selector_index: usize) {
238        self.stats.record_selector_acceptor_rejected(selector_index);
239        self.solver_scope
240            .stats_mut()
241            .record_selector_acceptor_rejected(selector_index);
242    }
243
244    pub fn record_moves_forager_ignored(&mut self, count: u64) {
245        self.stats.record_moves_forager_ignored(count);
246        self.solver_scope
247            .stats_mut()
248            .record_moves_forager_ignored(count);
249    }
250
251    pub fn record_move_hard_improving(&mut self) {
252        self.stats.record_move_hard_improving();
253        self.solver_scope.stats_mut().record_move_hard_improving();
254    }
255
256    pub fn record_move_hard_neutral(&mut self) {
257        self.stats.record_move_hard_neutral();
258        self.solver_scope.stats_mut().record_move_hard_neutral();
259    }
260
261    pub fn record_move_hard_worse(&mut self) {
262        self.stats.record_move_hard_worse();
263        self.solver_scope.stats_mut().record_move_hard_worse();
264    }
265
266    pub fn record_score_calculation(&mut self) {
267        self.stats.record_score_calculation();
268        self.solver_scope.record_score_calculation();
269    }
270
271    pub fn record_construction_slot_assigned(&mut self) {
272        self.stats.record_construction_slot_assigned();
273        self.solver_scope
274            .stats_mut()
275            .record_construction_slot_assigned();
276    }
277
278    pub fn record_construction_slot_kept(&mut self) {
279        self.stats.record_construction_slot_kept();
280        self.solver_scope
281            .stats_mut()
282            .record_construction_slot_kept();
283    }
284
285    pub fn record_construction_slot_no_doable(&mut self) {
286        self.stats.record_construction_slot_no_doable();
287        self.solver_scope
288            .stats_mut()
289            .record_construction_slot_no_doable();
290    }
291
292    pub fn record_scalar_assignment_required_remaining(
293        &mut self,
294        group_name: &'static str,
295        count: u64,
296    ) {
297        self.stats
298            .record_scalar_assignment_required_remaining(count);
299        self.solver_scope
300            .stats_mut()
301            .record_scalar_assignment_required_remaining(group_name, count);
302    }
303}