solverforge_solver/scope/
solver.rs1use std::sync::atomic::{AtomicBool, Ordering};
4use std::time::{Duration, Instant};
5
6use rand::rngs::StdRng;
7use rand::SeedableRng;
8
9use solverforge_core::domain::PlanningSolution;
10use solverforge_scoring::ScoreDirector;
11
12use crate::stats::SolverStats;
13
14pub struct SolverScope<'t, S: PlanningSolution, D: ScoreDirector<S>> {
23 score_director: D,
25 best_solution: Option<S>,
27 best_score: Option<S::Score>,
29 rng: StdRng,
31 start_time: Option<Instant>,
33 total_step_count: u64,
35 terminate: Option<&'t AtomicBool>,
37 stats: SolverStats,
39 time_limit: Option<Duration>,
41}
42
43impl<'t, S: PlanningSolution, D: ScoreDirector<S>> SolverScope<'t, S, D> {
44 pub fn new(score_director: D) -> Self {
46 Self {
47 score_director,
48 best_solution: None,
49 best_score: None,
50 rng: StdRng::from_os_rng(),
51 start_time: None,
52 total_step_count: 0,
53 terminate: None,
54 stats: SolverStats::default(),
55 time_limit: None,
56 }
57 }
58
59 pub fn with_terminate(score_director: D, terminate: Option<&'t AtomicBool>) -> Self {
61 Self {
62 score_director,
63 best_solution: None,
64 best_score: None,
65 rng: StdRng::from_os_rng(),
66 start_time: None,
67 total_step_count: 0,
68 terminate,
69 stats: SolverStats::default(),
70 time_limit: None,
71 }
72 }
73
74 pub fn with_seed(score_director: D, seed: u64) -> Self {
76 Self {
77 score_director,
78 best_solution: None,
79 best_score: None,
80 rng: StdRng::seed_from_u64(seed),
81 start_time: None,
82 total_step_count: 0,
83 terminate: None,
84 stats: SolverStats::default(),
85 time_limit: None,
86 }
87 }
88
89 pub fn start_solving(&mut self) {
91 self.start_time = Some(Instant::now());
92 self.total_step_count = 0;
93 self.stats.start();
94 }
95
96 pub fn elapsed(&self) -> Option<std::time::Duration> {
98 self.start_time.map(|t| t.elapsed())
99 }
100
101 pub fn score_director(&self) -> &D {
103 &self.score_director
104 }
105
106 pub fn score_director_mut(&mut self) -> &mut D {
108 &mut self.score_director
109 }
110
111 pub fn working_solution(&self) -> &S {
113 self.score_director.working_solution()
114 }
115
116 pub fn working_solution_mut(&mut self) -> &mut S {
118 self.score_director.working_solution_mut()
119 }
120
121 pub fn calculate_score(&mut self) -> S::Score {
123 self.score_director.calculate_score()
124 }
125
126 pub fn best_solution(&self) -> Option<&S> {
128 self.best_solution.as_ref()
129 }
130
131 pub fn best_score(&self) -> Option<&S::Score> {
133 self.best_score.as_ref()
134 }
135
136 pub fn update_best_solution(&mut self) {
138 let current_score = self.score_director.calculate_score();
139 let is_better = match &self.best_score {
140 None => true,
141 Some(best) => current_score > *best,
142 };
143
144 if is_better {
145 self.best_solution = Some(self.score_director.clone_working_solution());
146 self.best_score = Some(current_score);
147 }
148 }
149
150 pub fn set_best_solution(&mut self, solution: S, score: S::Score) {
152 self.best_solution = Some(solution);
153 self.best_score = Some(score);
154 }
155
156 pub fn rng(&mut self) -> &mut StdRng {
158 &mut self.rng
159 }
160
161 pub fn increment_step_count(&mut self) -> u64 {
163 self.total_step_count += 1;
164 self.total_step_count
165 }
166
167 pub fn total_step_count(&self) -> u64 {
169 self.total_step_count
170 }
171
172 pub fn take_best_solution(self) -> Option<S> {
174 self.best_solution
175 }
176
177 pub fn take_best_or_working_solution(self) -> S {
179 self.best_solution
180 .unwrap_or_else(|| self.score_director.clone_working_solution())
181 }
182
183 pub fn is_terminate_early(&self) -> bool {
185 self.terminate
186 .is_some_and(|flag| flag.load(Ordering::SeqCst))
187 }
188
189 pub fn set_time_limit(&mut self, limit: Duration) {
191 self.time_limit = Some(limit);
192 }
193
194 pub fn should_terminate(&self) -> bool {
196 if self.is_terminate_early() {
198 return true;
199 }
200 if let (Some(start), Some(limit)) = (self.start_time, self.time_limit) {
202 if start.elapsed() >= limit {
203 return true;
204 }
205 }
206 false
207 }
208
209 pub fn stats(&self) -> &SolverStats {
211 &self.stats
212 }
213
214 pub fn stats_mut(&mut self) -> &mut SolverStats {
216 &mut self.stats
217 }
218}