solverforge_solver/scope/
solver.rs1use std::sync::atomic::{AtomicBool, Ordering};
4use std::sync::Arc;
5use std::time::Instant;
6
7use rand::rngs::StdRng;
8use rand::SeedableRng;
9
10use solverforge_core::domain::PlanningSolution;
11use solverforge_scoring::ScoreDirector;
12
13use crate::statistics::StatisticsCollector;
14
15pub struct SolverScope<S: PlanningSolution> {
19 score_director: Box<dyn ScoreDirector<S>>,
21 best_solution: Option<S>,
23 best_score: Option<S::Score>,
25 rng: StdRng,
27 start_time: Option<Instant>,
29 total_step_count: u64,
31 statistics: Option<Arc<StatisticsCollector<S::Score>>>,
33 terminate_early_flag: Option<Arc<AtomicBool>>,
35}
36
37impl<S: PlanningSolution> SolverScope<S> {
38 pub fn new(score_director: Box<dyn ScoreDirector<S>>) -> Self {
40 Self {
41 score_director,
42 best_solution: None,
43 best_score: None,
44 rng: StdRng::from_os_rng(),
45 start_time: None,
46 total_step_count: 0,
47 statistics: None,
48 terminate_early_flag: None,
49 }
50 }
51
52 pub fn with_seed(score_director: Box<dyn ScoreDirector<S>>, seed: u64) -> Self {
54 Self {
55 score_director,
56 best_solution: None,
57 best_score: None,
58 rng: StdRng::seed_from_u64(seed),
59 start_time: None,
60 total_step_count: 0,
61 statistics: None,
62 terminate_early_flag: None,
63 }
64 }
65
66 pub fn with_statistics(mut self, collector: Arc<StatisticsCollector<S::Score>>) -> Self {
70 self.statistics = Some(collector);
71 self
72 }
73
74 pub fn statistics(&self) -> Option<&Arc<StatisticsCollector<S::Score>>> {
76 self.statistics.as_ref()
77 }
78
79 pub fn record_move(&self, accepted: bool) {
83 if let Some(stats) = &self.statistics {
84 stats.record_move(accepted);
85 }
86 }
87
88 pub fn record_score_calculation(&self) {
92 if let Some(stats) = &self.statistics {
93 stats.record_score_calculation();
94 }
95 }
96
97 pub fn start_solving(&mut self) {
99 self.start_time = Some(Instant::now());
100 self.total_step_count = 0;
101 }
102
103 pub fn elapsed(&self) -> Option<std::time::Duration> {
105 self.start_time.map(|t| t.elapsed())
106 }
107
108 pub fn score_director(&self) -> &dyn ScoreDirector<S> {
110 self.score_director.as_ref()
111 }
112
113 pub fn score_director_mut(&mut self) -> &mut dyn ScoreDirector<S> {
115 self.score_director.as_mut()
116 }
117
118 pub fn working_solution(&self) -> &S {
120 self.score_director.working_solution()
121 }
122
123 pub fn working_solution_mut(&mut self) -> &mut S {
125 self.score_director.working_solution_mut()
126 }
127
128 pub fn calculate_score(&mut self) -> S::Score {
130 self.score_director.calculate_score()
131 }
132
133 pub fn best_solution(&self) -> Option<&S> {
135 self.best_solution.as_ref()
136 }
137
138 pub fn best_score(&self) -> Option<&S::Score> {
140 self.best_score.as_ref()
141 }
142
143 pub fn update_best_solution(&mut self) {
145 let current_score = self.score_director.calculate_score();
146 let is_better = match &self.best_score {
147 None => true,
148 Some(best) => current_score > *best,
149 };
150
151 if is_better {
152 self.best_solution = Some(self.score_director.clone_working_solution());
153 self.best_score = Some(current_score.clone());
154
155 if let Some(stats) = &self.statistics {
157 stats.record_improvement(current_score);
158 }
159 }
160 }
161
162 pub fn set_best_solution(&mut self, solution: S, score: S::Score) {
164 self.best_solution = Some(solution);
165 self.best_score = Some(score);
166 }
167
168 pub fn rng(&mut self) -> &mut StdRng {
170 &mut self.rng
171 }
172
173 pub fn increment_step_count(&mut self) -> u64 {
175 self.total_step_count += 1;
176 self.total_step_count
177 }
178
179 pub fn total_step_count(&self) -> u64 {
181 self.total_step_count
182 }
183
184 pub fn take_best_solution(self) -> Option<S> {
186 self.best_solution
187 }
188
189 pub fn take_best_or_working_solution(self) -> S {
194 self.best_solution
195 .unwrap_or_else(|| self.score_director.clone_working_solution())
196 }
197
198 pub fn set_terminate_early_flag(&mut self, flag: Arc<AtomicBool>) {
202 self.terminate_early_flag = Some(flag);
203 }
204
205 pub fn is_terminate_early(&self) -> bool {
209 self.terminate_early_flag
210 .as_ref()
211 .is_some_and(|flag| flag.load(Ordering::SeqCst))
212 }
213}