solverforge_solver/heuristic/move/
list_ruin.rs1use std::fmt::Debug;
4
5use smallvec::SmallVec;
6use solverforge_core::domain::PlanningSolution;
7use solverforge_scoring::Director;
8
9use super::list_kernel::{
10 merged_ruin_sources, ruin_count, ruin_do_move, ruin_entity_indices, ruin_is_doable,
11 ruin_tabu_signature, ruin_undo_move, single_ruin_source, RuinSources, RuinUndo,
12 RuinValueTransfer, StaticListRuinAccess,
13};
14use super::{Move, MoveTabuSignature};
15
16pub struct ListRuinMove<S, V> {
23 entity_index: usize,
24 element_indices: SmallVec<[usize; 8]>,
25 sources: RuinSources,
26 entity_indices: SmallVec<[usize; 8]>,
27 access: StaticListRuinAccess<S, V>,
28 skip_empty_destinations: bool,
29}
30
31impl<S, V> Clone for ListRuinMove<S, V> {
32 fn clone(&self) -> Self {
33 Self {
34 entity_index: self.entity_index,
35 element_indices: self.element_indices.clone(),
36 sources: self.sources.clone(),
37 entity_indices: self.entity_indices.clone(),
38 access: self.access,
39 skip_empty_destinations: self.skip_empty_destinations,
40 }
41 }
42}
43
44impl<S, V: Debug> Debug for ListRuinMove<S, V> {
45 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 formatter
47 .debug_struct("ListRuinMove")
48 .field("sources", &self.sources)
49 .field("variable_name", &self.access.variable_name)
50 .finish()
51 }
52}
53
54impl<S, V> ListRuinMove<S, V> {
55 #[allow(clippy::too_many_arguments)]
56 pub fn new(
57 entity_index: usize,
58 element_indices: &[usize],
59 entity_count: fn(&S) -> usize,
60 list_len: fn(&S, usize) -> usize,
61 list_get: fn(&S, usize, usize) -> Option<V>,
62 list_remove: fn(&mut S, usize, usize) -> V,
63 list_insert: fn(&mut S, usize, usize, V),
64 variable_name: &'static str,
65 descriptor_index: usize,
66 ) -> Self {
67 Self::from_sources(
68 single_ruin_source(entity_index, element_indices),
69 entity_count,
70 list_len,
71 list_get,
72 list_remove,
73 list_insert,
74 variable_name,
75 descriptor_index,
76 )
77 }
78
79 #[allow(clippy::too_many_arguments)]
80 pub(crate) fn new_multi_source(
81 sources: &[(usize, SmallVec<[usize; 8]>)],
82 entity_count: fn(&S) -> usize,
83 list_len: fn(&S, usize) -> usize,
84 list_get: fn(&S, usize, usize) -> Option<V>,
85 list_remove: fn(&mut S, usize, usize) -> V,
86 list_insert: fn(&mut S, usize, usize, V),
87 variable_name: &'static str,
88 descriptor_index: usize,
89 ) -> Self {
90 Self::from_sources(
91 merged_ruin_sources(sources),
92 entity_count,
93 list_len,
94 list_get,
95 list_remove,
96 list_insert,
97 variable_name,
98 descriptor_index,
99 )
100 }
101
102 #[allow(clippy::too_many_arguments)]
103 fn from_sources(
104 sources: RuinSources,
105 entity_count: fn(&S) -> usize,
106 list_len: fn(&S, usize) -> usize,
107 list_get: fn(&S, usize, usize) -> Option<V>,
108 list_remove: fn(&mut S, usize, usize) -> V,
109 list_insert: fn(&mut S, usize, usize, V),
110 variable_name: &'static str,
111 descriptor_index: usize,
112 ) -> Self {
113 let (entity_index, element_indices) = sources
114 .first()
115 .cloned()
116 .unwrap_or_else(|| (0, SmallVec::new()));
117 let entity_indices = ruin_entity_indices(&sources);
118 Self {
119 entity_index,
120 element_indices,
121 sources,
122 entity_indices,
123 access: StaticListRuinAccess {
124 entity_count,
125 list_len,
126 list_get,
127 list_remove,
128 list_insert,
129 element_owner_fn: None,
130 precedence_element_count: None,
131 precedence_index_to_element: None,
132 precedence_successors_fn: None,
133 variable_name,
134 descriptor_index,
135 },
136 skip_empty_destinations: false,
137 }
138 }
139
140 pub fn with_element_owner_fn(
141 mut self,
142 element_owner_fn: Option<fn(&S, &V) -> Option<usize>>,
143 ) -> Self {
144 self.access.element_owner_fn = element_owner_fn;
145 self
146 }
147
148 pub(crate) fn with_precedence_hooks(
149 mut self,
150 element_count: Option<fn(&S) -> usize>,
151 index_to_element: Option<fn(&S, usize) -> V>,
152 successors_fn: Option<fn(&S, V, &mut Vec<V>)>,
153 ) -> Self {
154 self.access.precedence_element_count = element_count;
155 self.access.precedence_index_to_element = index_to_element;
156 self.access.precedence_successors_fn = successors_fn;
157 self
158 }
159
160 pub fn with_skip_empty_destinations(mut self, skip_empty_destinations: bool) -> Self {
161 self.skip_empty_destinations = skip_empty_destinations;
162 self
163 }
164
165 pub fn entity_index(&self) -> usize {
166 self.entity_index
167 }
168
169 pub fn element_indices(&self) -> &[usize] {
170 &self.element_indices
171 }
172
173 pub fn ruin_count(&self) -> usize {
174 ruin_count(&self.sources)
175 }
176}
177
178impl<S, V> Move<S> for ListRuinMove<S, V>
179where
180 S: PlanningSolution,
181 V: Clone + PartialEq + Send + Sync + Debug + 'static,
182{
183 type Undo = RuinUndo;
184
185 fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
186 ruin_is_doable(&self.access, &self.sources, score_director)
187 }
188
189 fn do_move<D: Director<S>>(&self, score_director: &mut D) -> Self::Undo {
190 ruin_do_move(
191 &self.access,
192 &self.sources,
193 self.skip_empty_destinations,
194 RuinValueTransfer::CloneBeforeInsert,
195 score_director,
196 )
197 }
198
199 fn undo_move<D: Director<S>>(&self, score_director: &mut D, undo: Self::Undo) {
200 ruin_undo_move(&self.access, &self.sources, undo, score_director);
201 }
202
203 fn descriptor_index(&self) -> usize {
204 self.access.descriptor_index
205 }
206
207 fn entity_indices(&self) -> &[usize] {
208 &self.entity_indices
209 }
210
211 fn variable_name(&self) -> &str {
212 self.access.variable_name
213 }
214
215 fn telemetry_label(&self) -> &'static str {
216 "list_ruin"
217 }
218
219 fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> MoveTabuSignature {
220 ruin_tabu_signature(
221 &self.access,
222 &self.sources,
223 &self.entity_indices,
224 score_director,
225 )
226 }
227}
228
229#[cfg(test)]
230pub(crate) fn final_positions_after_insertions(
231 placements: &SmallVec<[(usize, usize); 8]>,
232) -> SmallVec<[usize; 8]> {
233 super::list_kernel::final_positions_after_insertions(placements)
234}