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