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