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