solverforge_solver/phase/localsearch/acceptor/
late_acceptance.rs1use std::fmt::Debug;
4
5use solverforge_core::domain::PlanningSolution;
6
7use super::Acceptor;
8use crate::heuristic::r#move::MoveTabuSignature;
9
10pub struct LateAcceptanceAcceptor<S: PlanningSolution> {
33 late_acceptance_size: usize,
35 score_history: Vec<Option<S::Score>>,
37 current_index: usize,
39 best_score: Option<S::Score>,
42}
43
44impl<S: PlanningSolution> Debug for LateAcceptanceAcceptor<S> {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 f.debug_struct("LateAcceptanceAcceptor")
47 .field("late_acceptance_size", &self.late_acceptance_size)
48 .field("current_index", &self.current_index)
49 .finish()
50 }
51}
52
53impl<S: PlanningSolution> Clone for LateAcceptanceAcceptor<S> {
54 fn clone(&self) -> Self {
55 Self {
56 late_acceptance_size: self.late_acceptance_size,
57 score_history: self.score_history.clone(),
58 current_index: self.current_index,
59 best_score: self.best_score,
60 }
61 }
62}
63
64impl<S: PlanningSolution> LateAcceptanceAcceptor<S> {
65 pub fn new(late_acceptance_size: usize) -> Self {
74 assert!(
75 late_acceptance_size > 0,
76 "late_acceptance_size must be > 0, got 0"
77 );
78 Self {
79 late_acceptance_size,
80 score_history: vec![None; late_acceptance_size],
81 current_index: 0,
82 best_score: None,
83 }
84 }
85}
86
87impl<S: PlanningSolution> Default for LateAcceptanceAcceptor<S> {
88 fn default() -> Self {
89 Self::new(400)
90 }
91}
92
93impl<S: PlanningSolution> Acceptor<S> for LateAcceptanceAcceptor<S> {
94 fn is_accepted(
95 &mut self,
96 last_step_score: &S::Score,
97 move_score: &S::Score,
98 _move_signature: Option<&MoveTabuSignature>,
99 ) -> bool {
100 if move_score >= last_step_score {
102 return true;
103 }
104
105 if let Some(late_score) = &self.score_history[self.current_index] {
107 move_score >= late_score
108 } else {
109 true
111 }
112 }
113
114 fn phase_started(&mut self, initial_score: &S::Score) {
115 self.score_history.fill(Some(*initial_score));
117 self.current_index = 0;
118 self.best_score = Some(*initial_score);
119 }
120
121 fn step_ended(
122 &mut self,
123 step_score: &S::Score,
124 _accepted_move_signature: Option<&MoveTabuSignature>,
125 ) {
126 self.score_history[self.current_index] = Some(*step_score);
128 self.current_index = (self.current_index + 1) % self.late_acceptance_size;
129
130 let new_best = self.best_score.is_none_or(|best| step_score > &best);
133 if new_best {
134 self.best_score = Some(*step_score);
135 self.score_history.fill(Some(*step_score));
136 }
137 }
138}