solverforge_solver/
builder.rs1use solverforge_config::AcceptorConfig;
7use solverforge_core::domain::PlanningSolution;
8
9use crate::phase::localsearch::{
10 Acceptor, HillClimbingAcceptor, LateAcceptanceAcceptor, SimulatedAnnealingAcceptor,
11 TabuSearchAcceptor,
12};
13
14pub struct AcceptorBuilder;
16
17impl AcceptorBuilder {
18 pub fn build<S: PlanningSolution>(config: &AcceptorConfig) -> Box<dyn Acceptor<S>> {
20 match config {
21 AcceptorConfig::HillClimbing => Box::new(HillClimbingAcceptor::new()),
22
23 AcceptorConfig::TabuSearch(tabu_config) => {
24 let tabu_size = tabu_config
26 .entity_tabu_size
27 .or(tabu_config.move_tabu_size)
28 .unwrap_or(7);
29 Box::new(TabuSearchAcceptor::<S>::new(tabu_size))
30 }
31
32 AcceptorConfig::SimulatedAnnealing(sa_config) => {
33 let starting_temp = sa_config
35 .starting_temperature
36 .as_ref()
37 .and_then(|s| s.parse::<f64>().ok())
38 .unwrap_or(1.0);
39 Box::new(SimulatedAnnealingAcceptor::new(starting_temp, 0.99))
40 }
41
42 AcceptorConfig::LateAcceptance(la_config) => {
43 let size = la_config.late_acceptance_size.unwrap_or(400);
44 Box::new(LateAcceptanceAcceptor::<S>::new(size))
45 }
46
47 AcceptorConfig::GreatDeluge(_) => {
48 tracing::warn!("Great deluge acceptor not yet implemented, using hill climbing");
50 Box::new(HillClimbingAcceptor::new())
51 }
52 }
53 }
54
55 pub fn hill_climbing<S: PlanningSolution>() -> HillClimbingAcceptor {
57 HillClimbingAcceptor::new()
58 }
59
60 pub fn tabu_search<S: PlanningSolution>(tabu_size: usize) -> TabuSearchAcceptor<S> {
62 TabuSearchAcceptor::<S>::new(tabu_size)
63 }
64
65 pub fn simulated_annealing(starting_temp: f64, decay_rate: f64) -> SimulatedAnnealingAcceptor {
67 SimulatedAnnealingAcceptor::new(starting_temp, decay_rate)
68 }
69
70 pub fn late_acceptance<S: PlanningSolution>(size: usize) -> LateAcceptanceAcceptor<S> {
72 LateAcceptanceAcceptor::<S>::new(size)
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79 use solverforge_config::{
80 AcceptorConfig, LateAcceptanceConfig, SimulatedAnnealingConfig, TabuSearchConfig,
81 };
82 use solverforge_core::score::SimpleScore;
83
84 #[derive(Clone, Debug)]
85 struct TestSolution {
86 score: Option<SimpleScore>,
87 }
88
89 impl PlanningSolution for TestSolution {
90 type Score = SimpleScore;
91 fn score(&self) -> Option<Self::Score> {
92 self.score
93 }
94 fn set_score(&mut self, score: Option<Self::Score>) {
95 self.score = score;
96 }
97 }
98
99 #[test]
100 fn test_acceptor_builder_hill_climbing() {
101 let config = AcceptorConfig::HillClimbing;
102 let _acceptor: Box<dyn Acceptor<TestSolution>> = AcceptorBuilder::build(&config);
103 }
104
105 #[test]
106 fn test_acceptor_builder_tabu_search() {
107 let config = AcceptorConfig::TabuSearch(TabuSearchConfig {
108 entity_tabu_size: Some(10),
109 ..Default::default()
110 });
111 let _acceptor: Box<dyn Acceptor<TestSolution>> = AcceptorBuilder::build(&config);
112 }
113
114 #[test]
115 fn test_acceptor_builder_simulated_annealing() {
116 let config = AcceptorConfig::SimulatedAnnealing(SimulatedAnnealingConfig {
117 starting_temperature: Some("1.5".to_string()),
118 });
119 let _acceptor: Box<dyn Acceptor<TestSolution>> = AcceptorBuilder::build(&config);
120 }
121
122 #[test]
123 fn test_acceptor_builder_late_acceptance() {
124 let config = AcceptorConfig::LateAcceptance(LateAcceptanceConfig {
125 late_acceptance_size: Some(500),
126 });
127 let _acceptor: Box<dyn Acceptor<TestSolution>> = AcceptorBuilder::build(&config);
128 }
129}