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