solverforge_solver/heuristic/move/
list_permute.rs1use std::fmt::Debug;
4use std::marker::PhantomData;
5
6use smallvec::{smallvec, SmallVec};
7use solverforge_core::domain::PlanningSolution;
8use solverforge_scoring::Director;
9
10use super::list_kernel::{
11 permute_candidate_trace_identity, permute_do_move, permute_is_doable, permute_tabu_signature,
12 permute_undo_move, PermuteCoordinates, StaticListWindowAccess,
13};
14use super::{Move, MoveTabuSignature};
15
16pub const MAX_LIST_PERMUTE_WINDOW_SIZE: usize = 8;
17
18pub struct ListPermuteMove<S, V> {
19 entity_index: usize,
20 start: usize,
21 end: usize,
22 permutation: SmallVec<[usize; MAX_LIST_PERMUTE_WINDOW_SIZE]>,
23 inverse_permutation: SmallVec<[usize; MAX_LIST_PERMUTE_WINDOW_SIZE]>,
24 list_len: fn(&S, usize) -> usize,
25 list_get: fn(&S, usize, usize) -> Option<V>,
26 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
27 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
28 variable_name: &'static str,
29 descriptor_index: usize,
30 entity_indices: [usize; 1],
31 _phantom: PhantomData<fn() -> V>,
32}
33
34impl<S, V: Debug> Debug for ListPermuteMove<S, V> {
35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 f.debug_struct("ListPermuteMove")
37 .field("entity", &self.entity_index)
38 .field("start", &self.start)
39 .field("end", &self.end)
40 .field("permutation", &self.permutation)
41 .field("variable_name", &self.variable_name)
42 .finish()
43 }
44}
45
46impl<S, V> ListPermuteMove<S, V> {
47 #[allow(clippy::too_many_arguments)]
48 pub fn new(
49 entity_index: usize,
50 start: usize,
51 end: usize,
52 permutation: SmallVec<[usize; MAX_LIST_PERMUTE_WINDOW_SIZE]>,
53 list_len: fn(&S, usize) -> usize,
54 list_get: fn(&S, usize, usize) -> Option<V>,
55 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
56 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
57 variable_name: &'static str,
58 descriptor_index: usize,
59 ) -> Self {
60 let window_len = end.saturating_sub(start);
61 assert!(
62 (2..=MAX_LIST_PERMUTE_WINDOW_SIZE).contains(&window_len),
63 "list permute window length must be 2..={MAX_LIST_PERMUTE_WINDOW_SIZE}",
64 );
65 assert_eq!(
66 permutation.len(),
67 window_len,
68 "list permute permutation length must match window length",
69 );
70 let inverse_permutation = inverse_permutation(&permutation);
71 Self {
72 entity_index,
73 start,
74 end,
75 permutation,
76 inverse_permutation,
77 list_len,
78 list_get,
79 sublist_remove,
80 sublist_insert,
81 variable_name,
82 descriptor_index,
83 entity_indices: [entity_index],
84 _phantom: PhantomData,
85 }
86 }
87
88 #[inline]
89 pub fn entity_index(&self) -> usize {
90 self.entity_index
91 }
92
93 #[inline]
94 pub fn start(&self) -> usize {
95 self.start
96 }
97
98 #[inline]
99 pub fn end(&self) -> usize {
100 self.end
101 }
102
103 #[inline]
104 pub fn permutation(&self) -> &[usize] {
105 &self.permutation
106 }
107
108 fn access(&self) -> StaticListWindowAccess<S, V> {
109 StaticListWindowAccess {
110 list_len: self.list_len,
111 list_get: self.list_get,
112 sublist_remove: self.sublist_remove,
113 sublist_insert: self.sublist_insert,
114 variable_name: self.variable_name,
115 descriptor_index: self.descriptor_index,
116 }
117 }
118
119 fn coordinates(&self) -> PermuteCoordinates {
120 PermuteCoordinates {
121 entity: self.entity_index,
122 start: self.start,
123 end: self.end,
124 }
125 }
126}
127
128impl<S, V> Move<S> for ListPermuteMove<S, V>
129where
130 S: PlanningSolution,
131 V: Clone + Send + Sync + Debug + 'static,
132{
133 type Undo = SmallVec<[usize; MAX_LIST_PERMUTE_WINDOW_SIZE]>;
134
135 fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
136 permute_is_doable(
137 &self.access(),
138 self.coordinates(),
139 &self.permutation,
140 score_director,
141 )
142 }
143
144 fn do_move<D: Director<S>>(&self, score_director: &mut D) -> Self::Undo {
145 permute_do_move(
146 &self.access(),
147 self.coordinates(),
148 &self.permutation,
149 score_director,
150 );
151 self.inverse_permutation.clone()
152 }
153
154 fn undo_move<D: Director<S>>(&self, score_director: &mut D, undo: Self::Undo) {
155 permute_undo_move(&self.access(), self.coordinates(), &undo, score_director);
156 }
157
158 fn descriptor_index(&self) -> usize {
159 self.descriptor_index
160 }
161
162 fn entity_indices(&self) -> &[usize] {
163 &self.entity_indices
164 }
165
166 fn variable_name(&self) -> &str {
167 self.variable_name
168 }
169
170 fn telemetry_label(&self) -> &'static str {
171 "list_permute"
172 }
173
174 fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> MoveTabuSignature {
175 permute_tabu_signature(
176 &self.access(),
177 self.coordinates(),
178 &self.permutation,
179 &self.inverse_permutation,
180 score_director,
181 )
182 }
183
184 fn candidate_trace_identity(&self) -> Option<crate::stats::CandidateTraceIdentity> {
185 Some(permute_candidate_trace_identity(
186 &self.access(),
187 self.coordinates(),
188 &self.permutation,
189 ))
190 }
191}
192
193fn inverse_permutation(permutation: &[usize]) -> SmallVec<[usize; MAX_LIST_PERMUTE_WINDOW_SIZE]> {
194 let mut inverse = smallvec![0; permutation.len()];
195 for (new_idx, &old_idx) in permutation.iter().enumerate() {
196 inverse[old_idx] = new_idx;
197 }
198 inverse
199}