solverforge_solver/builder/
acceptor.rs1use std::fmt::Debug;
4
5use solverforge_config::AcceptorConfig;
6use solverforge_core::domain::PlanningSolution;
7use solverforge_core::score::Score;
8
9use crate::phase::localsearch::{
10 Acceptor, GreatDelugeAcceptor, HillClimbingAcceptor, LateAcceptanceAcceptor,
11 SimulatedAnnealingAcceptor, TabuSearchAcceptor,
12};
13
14#[allow(clippy::large_enum_variant)]
19pub enum AnyAcceptor<S: PlanningSolution> {
20 HillClimbing(HillClimbingAcceptor),
22 TabuSearch(TabuSearchAcceptor<S>),
24 SimulatedAnnealing(SimulatedAnnealingAcceptor),
26 LateAcceptance(LateAcceptanceAcceptor<S>),
28 GreatDeluge(GreatDelugeAcceptor<S>),
30}
31
32impl<S: PlanningSolution> Debug for AnyAcceptor<S> {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 match self {
35 Self::HillClimbing(a) => write!(f, "AnyAcceptor::HillClimbing({a:?})"),
36 Self::TabuSearch(a) => write!(f, "AnyAcceptor::TabuSearch({a:?})"),
37 Self::SimulatedAnnealing(a) => write!(f, "AnyAcceptor::SimulatedAnnealing({a:?})"),
38 Self::LateAcceptance(a) => write!(f, "AnyAcceptor::LateAcceptance({a:?})"),
39 Self::GreatDeluge(a) => write!(f, "AnyAcceptor::GreatDeluge({a:?})"),
40 }
41 }
42}
43
44impl<S: PlanningSolution> Clone for AnyAcceptor<S>
45where
46 S::Score: Clone,
47{
48 fn clone(&self) -> Self {
49 match self {
50 Self::HillClimbing(a) => Self::HillClimbing(a.clone()),
51 Self::TabuSearch(a) => Self::TabuSearch(a.clone()),
52 Self::SimulatedAnnealing(a) => Self::SimulatedAnnealing(a.clone()),
53 Self::LateAcceptance(a) => Self::LateAcceptance(a.clone()),
54 Self::GreatDeluge(a) => Self::GreatDeluge(a.clone()),
55 }
56 }
57}
58
59impl<S: PlanningSolution> Acceptor<S> for AnyAcceptor<S>
60where
61 S::Score: Score,
62{
63 fn is_accepted(&mut self, last_step_score: &S::Score, move_score: &S::Score) -> bool {
64 match self {
65 Self::HillClimbing(a) => Acceptor::<S>::is_accepted(a, last_step_score, move_score),
66 Self::TabuSearch(a) => Acceptor::<S>::is_accepted(a, last_step_score, move_score),
67 Self::SimulatedAnnealing(a) => {
68 Acceptor::<S>::is_accepted(a, last_step_score, move_score)
69 }
70 Self::LateAcceptance(a) => Acceptor::<S>::is_accepted(a, last_step_score, move_score),
71 Self::GreatDeluge(a) => Acceptor::<S>::is_accepted(a, last_step_score, move_score),
72 }
73 }
74
75 fn phase_started(&mut self, initial_score: &S::Score) {
76 match self {
77 Self::HillClimbing(a) => Acceptor::<S>::phase_started(a, initial_score),
78 Self::TabuSearch(a) => Acceptor::<S>::phase_started(a, initial_score),
79 Self::SimulatedAnnealing(a) => Acceptor::<S>::phase_started(a, initial_score),
80 Self::LateAcceptance(a) => Acceptor::<S>::phase_started(a, initial_score),
81 Self::GreatDeluge(a) => Acceptor::<S>::phase_started(a, initial_score),
82 }
83 }
84
85 fn phase_ended(&mut self) {
86 match self {
87 Self::HillClimbing(a) => Acceptor::<S>::phase_ended(a),
88 Self::TabuSearch(a) => Acceptor::<S>::phase_ended(a),
89 Self::SimulatedAnnealing(a) => Acceptor::<S>::phase_ended(a),
90 Self::LateAcceptance(a) => Acceptor::<S>::phase_ended(a),
91 Self::GreatDeluge(a) => Acceptor::<S>::phase_ended(a),
92 }
93 }
94
95 fn step_started(&mut self) {
96 match self {
97 Self::HillClimbing(a) => Acceptor::<S>::step_started(a),
98 Self::TabuSearch(a) => Acceptor::<S>::step_started(a),
99 Self::SimulatedAnnealing(a) => Acceptor::<S>::step_started(a),
100 Self::LateAcceptance(a) => Acceptor::<S>::step_started(a),
101 Self::GreatDeluge(a) => Acceptor::<S>::step_started(a),
102 }
103 }
104
105 fn step_ended(&mut self, step_score: &S::Score) {
106 match self {
107 Self::HillClimbing(a) => Acceptor::<S>::step_ended(a, step_score),
108 Self::TabuSearch(a) => Acceptor::<S>::step_ended(a, step_score),
109 Self::SimulatedAnnealing(a) => Acceptor::<S>::step_ended(a, step_score),
110 Self::LateAcceptance(a) => Acceptor::<S>::step_ended(a, step_score),
111 Self::GreatDeluge(a) => Acceptor::<S>::step_ended(a, step_score),
112 }
113 }
114}
115
116pub struct AcceptorBuilder;
118
119impl AcceptorBuilder {
120 pub fn build<S: PlanningSolution>(config: &AcceptorConfig) -> AnyAcceptor<S>
122 where
123 S::Score: Score,
124 {
125 match config {
126 AcceptorConfig::HillClimbing => AnyAcceptor::HillClimbing(HillClimbingAcceptor::new()),
127
128 AcceptorConfig::TabuSearch(tabu_config) => {
129 let tabu_size = tabu_config
130 .entity_tabu_size
131 .or(tabu_config.move_tabu_size)
132 .unwrap_or(7);
133 AnyAcceptor::TabuSearch(TabuSearchAcceptor::<S>::new(tabu_size))
134 }
135
136 AcceptorConfig::SimulatedAnnealing(sa_config) => {
137 let starting_temp = sa_config
138 .starting_temperature
139 .as_ref()
140 .and_then(|s| s.parse::<f64>().ok())
141 .unwrap_or(0.0);
142 AnyAcceptor::SimulatedAnnealing(SimulatedAnnealingAcceptor::new(
143 starting_temp,
144 0.999985,
145 ))
146 }
147
148 AcceptorConfig::LateAcceptance(la_config) => {
149 let size = la_config.late_acceptance_size.unwrap_or(400);
150 AnyAcceptor::LateAcceptance(LateAcceptanceAcceptor::<S>::new(size))
151 }
152
153 AcceptorConfig::GreatDeluge(gd_config) => {
154 let rain_speed = gd_config.water_level_increase_ratio.unwrap_or(0.001);
155 AnyAcceptor::GreatDeluge(GreatDelugeAcceptor::<S>::new(rain_speed))
156 }
157 }
158 }
159
160 pub fn hill_climbing<S: PlanningSolution>() -> HillClimbingAcceptor {
162 HillClimbingAcceptor::new()
163 }
164
165 pub fn tabu_search<S: PlanningSolution>(tabu_size: usize) -> TabuSearchAcceptor<S> {
167 TabuSearchAcceptor::<S>::new(tabu_size)
168 }
169
170 pub fn simulated_annealing(starting_temp: f64, decay_rate: f64) -> SimulatedAnnealingAcceptor {
172 SimulatedAnnealingAcceptor::new(starting_temp, decay_rate)
173 }
174
175 pub fn late_acceptance<S: PlanningSolution>(size: usize) -> LateAcceptanceAcceptor<S> {
177 LateAcceptanceAcceptor::<S>::new(size)
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184 use solverforge_config::{
185 AcceptorConfig, LateAcceptanceConfig, SimulatedAnnealingConfig, TabuSearchConfig,
186 };
187 use solverforge_core::score::SoftScore;
188
189 #[derive(Clone, Debug)]
190 struct TestSolution {
191 score: Option<SoftScore>,
192 }
193
194 impl PlanningSolution for TestSolution {
195 type Score = SoftScore;
196 fn score(&self) -> Option<Self::Score> {
197 self.score
198 }
199 fn set_score(&mut self, score: Option<Self::Score>) {
200 self.score = score;
201 }
202 }
203
204 #[test]
205 fn test_acceptor_builder_hill_climbing() {
206 let config = AcceptorConfig::HillClimbing;
207 let _acceptor: AnyAcceptor<TestSolution> = AcceptorBuilder::build(&config);
208 }
209
210 #[test]
211 fn test_acceptor_builder_tabu_search() {
212 let config = AcceptorConfig::TabuSearch(TabuSearchConfig {
213 entity_tabu_size: Some(10),
214 ..Default::default()
215 });
216 let _acceptor: AnyAcceptor<TestSolution> = AcceptorBuilder::build(&config);
217 }
218
219 #[test]
220 fn test_acceptor_builder_simulated_annealing() {
221 let config = AcceptorConfig::SimulatedAnnealing(SimulatedAnnealingConfig {
222 starting_temperature: Some("1.5".to_string()),
223 });
224 let _acceptor: AnyAcceptor<TestSolution> = AcceptorBuilder::build(&config);
225 }
226
227 #[test]
228 fn test_acceptor_builder_late_acceptance() {
229 let config = AcceptorConfig::LateAcceptance(LateAcceptanceConfig {
230 late_acceptance_size: Some(500),
231 });
232 let _acceptor: AnyAcceptor<TestSolution> = AcceptorBuilder::build(&config);
233 }
234}