solverforge_solver/
builder.rs

1//! Builder module for constructing solver components from configuration
2//!
3//! This module provides the wiring between configuration types and
4//! the actual solver implementation.
5
6use solverforge_config::AcceptorConfig;
7use solverforge_core::domain::PlanningSolution;
8
9use crate::phase::localsearch::{
10    Acceptor, HillClimbingAcceptor, LateAcceptanceAcceptor, SimulatedAnnealingAcceptor,
11    TabuSearchAcceptor,
12};
13
14/// Builder for constructing acceptors from configuration.
15pub struct AcceptorBuilder;
16
17impl AcceptorBuilder {
18    /// Builds an acceptor from configuration.
19    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                // Use entity tabu size if specified, otherwise default
25                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                // Parse starting temperature (default to 1.0 if not specified)
34                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                // Great deluge not yet implemented, fall back to hill climbing
49                tracing::warn!("Great deluge acceptor not yet implemented, using hill climbing");
50                Box::new(HillClimbingAcceptor::new())
51            }
52        }
53    }
54
55    /// Creates a default hill climbing acceptor.
56    pub fn hill_climbing<S: PlanningSolution>() -> HillClimbingAcceptor {
57        HillClimbingAcceptor::new()
58    }
59
60    /// Creates a tabu search acceptor with the given size.
61    pub fn tabu_search<S: PlanningSolution>(tabu_size: usize) -> TabuSearchAcceptor<S> {
62        TabuSearchAcceptor::<S>::new(tabu_size)
63    }
64
65    /// Creates a simulated annealing acceptor.
66    pub fn simulated_annealing(starting_temp: f64, decay_rate: f64) -> SimulatedAnnealingAcceptor {
67        SimulatedAnnealingAcceptor::new(starting_temp, decay_rate)
68    }
69
70    /// Creates a late acceptance acceptor.
71    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}