solverforge_solver/phase/localsearch/acceptor/
diversified_late_acceptance.rs1use std::fmt::Debug;
4
5use solverforge_core::domain::PlanningSolution;
6use solverforge_core::Score;
7
8use super::Acceptor;
9use crate::heuristic::r#move::MoveTabuSignature;
10
11pub struct DiversifiedLateAcceptanceAcceptor<S: PlanningSolution> {
41 late_acceptance_size: usize,
43 score_history: Vec<Option<S::Score>>,
45 current_index: usize,
47 best_score: Option<S::Score>,
49 tolerance: f64,
51}
52
53impl<S: PlanningSolution> Debug for DiversifiedLateAcceptanceAcceptor<S> {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 f.debug_struct("DiversifiedLateAcceptanceAcceptor")
56 .field("late_acceptance_size", &self.late_acceptance_size)
57 .field("current_index", &self.current_index)
58 .field("tolerance", &self.tolerance)
59 .finish()
60 }
61}
62
63impl<S: PlanningSolution> Clone for DiversifiedLateAcceptanceAcceptor<S> {
64 fn clone(&self) -> Self {
65 Self {
66 late_acceptance_size: self.late_acceptance_size,
67 score_history: self.score_history.clone(),
68 current_index: self.current_index,
69 best_score: self.best_score,
70 tolerance: self.tolerance,
71 }
72 }
73}
74
75impl<S: PlanningSolution> DiversifiedLateAcceptanceAcceptor<S> {
76 pub fn new(late_acceptance_size: usize, tolerance: f64) -> Self {
86 assert!(
87 late_acceptance_size > 0,
88 "late_acceptance_size must be > 0, got 0"
89 );
90 Self {
91 late_acceptance_size,
92 score_history: vec![None; late_acceptance_size],
93 current_index: 0,
94 best_score: None,
95 tolerance,
96 }
97 }
98
99 pub fn with_default_tolerance(late_acceptance_size: usize) -> Self {
101 Self::new(late_acceptance_size, 0.01)
102 }
103}
104
105impl<S: PlanningSolution> Default for DiversifiedLateAcceptanceAcceptor<S> {
106 fn default() -> Self {
107 Self::new(400, 0.01)
108 }
109}
110
111impl<S: PlanningSolution> Acceptor<S> for DiversifiedLateAcceptanceAcceptor<S> {
112 fn is_accepted(
113 &mut self,
114 last_step_score: &S::Score,
115 move_score: &S::Score,
116 _move_signature: Option<&MoveTabuSignature>,
117 ) -> bool {
118 if move_score >= last_step_score {
120 return true;
121 }
122
123 if let Some(late_score) = &self.score_history[self.current_index] {
125 if move_score >= late_score {
126 return true;
127 }
128 } else {
129 return true;
131 }
132
133 if let Some(best) = &self.best_score {
135 let abs_best = best.abs();
137 let threshold = *best - abs_best.multiply(self.tolerance);
138 if move_score >= &threshold {
139 return true;
140 }
141 }
142
143 false
144 }
145
146 fn phase_started(&mut self, initial_score: &S::Score) {
147 for slot in &mut self.score_history {
149 *slot = Some(*initial_score);
150 }
151 self.current_index = 0;
152 self.best_score = Some(*initial_score);
153 }
154
155 fn step_ended(
156 &mut self,
157 step_score: &S::Score,
158 _accepted_move_signature: Option<&MoveTabuSignature>,
159 ) {
160 if let Some(best) = &self.best_score {
162 if step_score > best {
163 self.best_score = Some(*step_score);
164 }
165 } else {
166 self.best_score = Some(*step_score);
167 }
168
169 self.score_history[self.current_index] = Some(*step_score);
171 self.current_index = (self.current_index + 1) % self.late_acceptance_size;
172 }
173}
174
175#[cfg(test)]
176mod tests;