Skip to main content

solverforge_solver/manager/phase_factory/
local_search.rs

1// Local search phase factory with zero type erasure.
2
3use std::marker::PhantomData;
4
5use solverforge_core::domain::PlanningSolution;
6use solverforge_scoring::Director;
7
8use crate::heuristic::{Move, MoveSelector};
9use crate::phase::localsearch::{
10    AcceptedCountForager, Acceptor, HillClimbingAcceptor, LateAcceptanceAcceptor,
11    LocalSearchForager, LocalSearchPhase, SimulatedAnnealingAcceptor, TabuSearchAcceptor,
12    TabuSearchPolicy,
13};
14
15use super::super::PhaseFactory;
16
17/// Zero-erasure factory for local search phases.
18///
19/// All types flow through generics - MoveSelector `MS`, Acceptor `A`,
20/// and Forager `Fo` are all concrete types.
21///
22/// # Type Parameters
23///
24/// * `S` - The planning solution type
25/// * `M` - The move type
26/// * `MS` - The move selector type (concrete)
27/// * `A` - The acceptor type (concrete)
28/// * `Fo` - The forager type (concrete)
29pub struct LocalSearchPhaseFactory<S, M, MS, A, Fo>
30where
31    S: PlanningSolution,
32    M: Move<S>,
33    MS: MoveSelector<S, M>,
34    A: Acceptor<S>,
35    Fo: LocalSearchForager<S, M>,
36{
37    move_selector: MS,
38    acceptor: A,
39    forager: Fo,
40    step_limit: Option<u64>,
41    _marker: PhantomData<fn() -> (S, M)>,
42}
43
44impl<S, M, MS, A, Fo> LocalSearchPhaseFactory<S, M, MS, A, Fo>
45where
46    S: PlanningSolution,
47    M: Move<S>,
48    MS: MoveSelector<S, M>,
49    A: Acceptor<S>,
50    Fo: LocalSearchForager<S, M>,
51{
52    pub fn new(move_selector: MS, acceptor: A, forager: Fo) -> Self {
53        Self {
54            move_selector,
55            acceptor,
56            forager,
57            step_limit: None,
58            _marker: PhantomData,
59        }
60    }
61
62    pub fn with_step_limit(mut self, limit: u64) -> Self {
63        self.step_limit = Some(limit);
64        self
65    }
66}
67
68// Convenience constructors with specific acceptors
69
70impl<S, M, MS> LocalSearchPhaseFactory<S, M, MS, HillClimbingAcceptor, AcceptedCountForager<S>>
71where
72    S: PlanningSolution,
73    M: Move<S>,
74    MS: MoveSelector<S, M>,
75{
76    pub fn hill_climbing(move_selector: MS) -> Self {
77        Self::new(
78            move_selector,
79            HillClimbingAcceptor::new(),
80            AcceptedCountForager::new(1),
81        )
82    }
83}
84
85impl<S, M, MS> LocalSearchPhaseFactory<S, M, MS, TabuSearchAcceptor<S>, AcceptedCountForager<S>>
86where
87    S: PlanningSolution,
88    M: Move<S>,
89    MS: MoveSelector<S, M>,
90{
91    pub fn tabu_search(move_selector: MS, tabu_size: usize) -> Self {
92        Self::new(
93            move_selector,
94            TabuSearchAcceptor::new(TabuSearchPolicy::move_only(tabu_size)),
95            AcceptedCountForager::new(1),
96        )
97    }
98}
99
100impl<S, M, MS>
101    LocalSearchPhaseFactory<S, M, MS, SimulatedAnnealingAcceptor, AcceptedCountForager<S>>
102where
103    S: PlanningSolution,
104    M: Move<S>,
105    MS: MoveSelector<S, M>,
106{
107    pub fn simulated_annealing(move_selector: MS, starting_temp: f64, decay_rate: f64) -> Self {
108        Self::new(
109            move_selector,
110            SimulatedAnnealingAcceptor::new(starting_temp, decay_rate),
111            AcceptedCountForager::new(1),
112        )
113    }
114}
115
116impl<S, M, MS> LocalSearchPhaseFactory<S, M, MS, LateAcceptanceAcceptor<S>, AcceptedCountForager<S>>
117where
118    S: PlanningSolution,
119    M: Move<S>,
120    MS: MoveSelector<S, M>,
121{
122    pub fn late_acceptance(move_selector: MS, size: usize) -> Self {
123        Self::new(
124            move_selector,
125            LateAcceptanceAcceptor::new(size),
126            AcceptedCountForager::new(1),
127        )
128    }
129}
130
131impl<S, D, M, MS, A, Fo> PhaseFactory<S, D> for LocalSearchPhaseFactory<S, M, MS, A, Fo>
132where
133    S: PlanningSolution,
134    D: Director<S>,
135    M: Move<S> + Send + Sync + 'static,
136    MS: MoveSelector<S, M> + Clone + Send + Sync + 'static,
137    A: Acceptor<S> + Clone + Send + Sync + 'static,
138    Fo: LocalSearchForager<S, M> + Clone + Send + Sync + 'static,
139{
140    type Phase = LocalSearchPhase<S, M, MS, A, Fo>;
141
142    fn create(&self) -> Self::Phase {
143        LocalSearchPhase::new(
144            self.move_selector.clone(),
145            self.acceptor.clone(),
146            self.forager.clone(),
147            self.step_limit,
148        )
149    }
150}