solverforge_solver/phase/localsearch/acceptor/
diversified_late_acceptance.rs1use std::fmt::Debug;
5
6use solverforge_core::domain::PlanningSolution;
7use solverforge_core::Score;
8
9use super::Acceptor;
10use crate::heuristic::r#move::MoveTabuSignature;
11
12pub struct DiversifiedLateAcceptanceAcceptor<S: PlanningSolution> {
13 late_acceptance_size: usize,
14 previous_scores: Vec<S::Score>,
15 late_score_index: usize,
16 late_worse_score: S::Score,
17 late_worse_occurrences: usize,
18}
19
20impl<S: PlanningSolution> Debug for DiversifiedLateAcceptanceAcceptor<S> {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 f.debug_struct("DiversifiedLateAcceptanceAcceptor")
23 .field("late_acceptance_size", &self.late_acceptance_size)
24 .field("late_score_index", &self.late_score_index)
25 .field("late_worse_occurrences", &self.late_worse_occurrences)
26 .finish()
27 }
28}
29
30impl<S: PlanningSolution> Clone for DiversifiedLateAcceptanceAcceptor<S> {
31 fn clone(&self) -> Self {
32 Self {
33 late_acceptance_size: self.late_acceptance_size,
34 previous_scores: self.previous_scores.clone(),
35 late_score_index: self.late_score_index,
36 late_worse_score: self.late_worse_score,
37 late_worse_occurrences: self.late_worse_occurrences,
38 }
39 }
40}
41
42impl<S: PlanningSolution> DiversifiedLateAcceptanceAcceptor<S> {
43 pub fn new(late_acceptance_size: usize) -> Self {
44 assert!(
45 late_acceptance_size > 0,
46 "late_acceptance_size must be > 0, got 0"
47 );
48 Self {
49 late_acceptance_size,
50 previous_scores: Vec::new(),
51 late_score_index: 0,
52 late_worse_score: S::Score::zero(),
53 late_worse_occurrences: late_acceptance_size,
54 }
55 }
56}
57
58impl<S: PlanningSolution> Default for DiversifiedLateAcceptanceAcceptor<S> {
59 fn default() -> Self {
60 Self::new(400)
61 }
62}
63
64impl<S: PlanningSolution> Acceptor<S> for DiversifiedLateAcceptanceAcceptor<S> {
65 fn is_accepted(
66 &mut self,
67 last_step_score: &S::Score,
68 move_score: &S::Score,
69 _move_signature: Option<&MoveTabuSignature>,
70 ) -> bool {
71 if self.previous_scores.is_empty() {
72 return *move_score >= *last_step_score;
75 }
76
77 let accepted = move_score == last_step_score || *move_score > self.late_worse_score;
79 let current = if accepted {
80 *move_score
81 } else {
82 *last_step_score
83 };
84
85 let late_score = self.previous_scores[self.late_score_index];
86 let current_score_worse = current < late_score;
87 let current_score_better = current > late_score && current > *last_step_score;
88 if current_score_worse || current_score_better {
89 self.update_late_score(current);
90 }
91 self.late_score_index = (self.late_score_index + 1) % self.late_acceptance_size;
92 accepted
93 }
94
95 fn phase_started(&mut self, initial_score: &S::Score) {
96 self.previous_scores = vec![*initial_score; self.late_acceptance_size];
97 self.late_score_index = 0;
98 self.late_worse_occurrences = self.late_acceptance_size;
99 self.late_worse_score = *initial_score;
100 }
101
102 fn step_ended(
103 &mut self,
104 _step_score: &S::Score,
105 _accepted_move_signature: Option<&MoveTabuSignature>,
106 ) {
107 }
108}
109
110impl<S: PlanningSolution> DiversifiedLateAcceptanceAcceptor<S> {
111 fn update_late_score(&mut self, new_score: S::Score) {
112 let slot_score = self.previous_scores[self.late_score_index];
113 let slot_equal_to_worse = slot_score == self.late_worse_score;
114
115 match new_score.cmp(&self.late_worse_score) {
116 std::cmp::Ordering::Less => {
117 self.late_worse_score = new_score;
118 self.late_worse_occurrences = 1;
119 }
120 std::cmp::Ordering::Equal => {
121 if !slot_equal_to_worse {
122 self.late_worse_occurrences += 1;
123 }
124 }
125 std::cmp::Ordering::Greater => {
126 if slot_equal_to_worse {
127 self.late_worse_occurrences -= 1;
128 }
129 }
130 }
131 self.previous_scores[self.late_score_index] = new_score;
132
133 if self.late_worse_occurrences == 0 {
134 let mut worse = self.previous_scores[0];
135 let mut occurrences = 1;
136 for score in self.previous_scores.iter().skip(1) {
137 match (*score).cmp(&worse) {
138 std::cmp::Ordering::Less => {
139 worse = *score;
140 occurrences = 1;
141 }
142 std::cmp::Ordering::Equal => occurrences += 1,
143 std::cmp::Ordering::Greater => {}
144 }
145 }
146 self.late_worse_score = worse;
147 self.late_worse_occurrences = occurrences;
148 }
149 }
150}
151
152#[cfg(test)]
153mod tests;