Skip to main content

solverforge_solver/heuristic/move/
list_reverse.rs

1/* ListReverseMove - reverses a segment within a list variable.
2
3This move reverses the order of elements in a range. Essential for
4TSP 2-opt optimization where reversing a tour segment can reduce distance.
5
6# Zero-Erasure Design
7
8Uses concrete function pointers for list operations. No `dyn Any`, no downcasting.
9*/
10
11use std::borrow::Cow;
12use std::fmt::Debug;
13use std::marker::PhantomData;
14
15use solverforge_core::domain::PlanningSolution;
16use solverforge_scoring::Director;
17
18use super::list_kernel::{
19    reverse_candidate_trace_identity, reverse_do_move, reverse_is_doable, reverse_tabu_signature,
20    ReverseCoordinates, StaticListReverseAccess,
21};
22use super::{Move, MoveTabuSignature};
23
24/// A move that reverses a segment within a list.
25///
26/// This is the fundamental 2-opt move for TSP. Reversing a segment of the tour
27/// can significantly reduce total distance by eliminating crossing edges.
28///
29/// # Type Parameters
30/// * `S` - The planning solution type
31/// * `V` - The list element value type
32///
33/// # Example
34///
35/// ```
36/// use solverforge_solver::heuristic::r#move::ListReverseMove;
37/// use solverforge_core::domain::PlanningSolution;
38/// use solverforge_core::score::SoftScore;
39///
40/// #[derive(Clone, Debug)]
41/// struct Tour { cities: Vec<i32>, score: Option<SoftScore> }
42///
43/// impl PlanningSolution for Tour {
44///     type Score = SoftScore;
45///     fn score(&self) -> Option<Self::Score> { self.score }
46///     fn set_score(&mut self, score: Option<Self::Score>) { self.score = score; }
47/// }
48///
49/// fn list_len(s: &Tour, _: usize) -> usize { s.cities.len() }
50/// fn list_get(s: &Tour, _: usize, pos: usize) -> Option<i32> { s.cities.get(pos).copied() }
51/// fn list_reverse(s: &mut Tour, _: usize, start: usize, end: usize) {
52///     s.cities[start..end].reverse();
53/// }
54///
55/// // Reverse segment [1..4) in tour: [A, B, C, D, E] -> [A, D, C, B, E]
56/// let m = ListReverseMove::<Tour, i32>::new(
57///     0, 1, 4,
58///     list_len, list_get, list_reverse,
59///     "cities", 0,
60/// );
61/// ```
62pub struct ListReverseMove<S, V> {
63    // Entity index
64    entity_index: usize,
65    // Start of range to reverse (inclusive)
66    start: usize,
67    // End of range to reverse (exclusive)
68    end: usize,
69    list_len: fn(&S, usize) -> usize,
70    list_get: fn(&S, usize, usize) -> Option<V>,
71    // Reverse elements in range [start, end)
72    list_reverse: fn(&mut S, usize, usize, usize),
73    variable_name: &'static str,
74    descriptor_index: usize,
75    _phantom: PhantomData<fn() -> V>,
76}
77
78impl<S, V> Clone for ListReverseMove<S, V> {
79    fn clone(&self) -> Self {
80        *self
81    }
82}
83
84impl<S, V> Copy for ListReverseMove<S, V> {}
85
86impl<S, V: Debug> Debug for ListReverseMove<S, V> {
87    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88        f.debug_struct("ListReverseMove")
89            .field("entity", &self.entity_index)
90            .field("range", &(self.start..self.end))
91            .field("variable_name", &self.variable_name)
92            .finish()
93    }
94}
95
96impl<S, V> ListReverseMove<S, V> {
97    /* Creates a new list reverse move with concrete function pointers.
98
99    # Arguments
100    * `entity_index` - Entity index
101    * `start` - Start of range (inclusive)
102    * `end` - End of range (exclusive)
103    * `list_len` - Function to get list length
104    * `list_reverse` - Function to reverse elements in range
105    * `variable_name` - Name of the list variable
106    * `descriptor_index` - Entity descriptor index
107    */
108    #[allow(clippy::too_many_arguments)]
109    pub fn new(
110        entity_index: usize,
111        start: usize,
112        end: usize,
113        list_len: fn(&S, usize) -> usize,
114        list_get: fn(&S, usize, usize) -> Option<V>,
115        list_reverse: fn(&mut S, usize, usize, usize),
116        variable_name: &'static str,
117        descriptor_index: usize,
118    ) -> Self {
119        Self {
120            entity_index,
121            start,
122            end,
123            list_len,
124            list_get,
125            list_reverse,
126            variable_name,
127            descriptor_index,
128            _phantom: PhantomData,
129        }
130    }
131
132    pub fn entity_index(&self) -> usize {
133        self.entity_index
134    }
135
136    pub fn start(&self) -> usize {
137        self.start
138    }
139
140    pub fn end(&self) -> usize {
141        self.end
142    }
143
144    pub fn segment_len(&self) -> usize {
145        self.end.saturating_sub(self.start)
146    }
147
148    fn access(&self) -> StaticListReverseAccess<S, V> {
149        StaticListReverseAccess {
150            list_len: self.list_len,
151            list_get: self.list_get,
152            list_reverse: self.list_reverse,
153            variable_name: self.variable_name,
154            descriptor_index: self.descriptor_index,
155        }
156    }
157
158    fn coordinates(&self) -> ReverseCoordinates {
159        ReverseCoordinates {
160            entity: self.entity_index,
161            start: self.start,
162            end: self.end,
163        }
164    }
165}
166
167impl<S, V> Move<S> for ListReverseMove<S, V>
168where
169    S: PlanningSolution,
170    V: Clone + Send + Sync + Debug + 'static,
171{
172    type Undo = ();
173
174    fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
175        reverse_is_doable(&self.access(), self.coordinates(), score_director)
176    }
177
178    fn do_move<D: Director<S>>(&self, score_director: &mut D) -> Self::Undo {
179        reverse_do_move(&self.access(), self.coordinates(), score_director);
180    }
181
182    fn undo_move<D: Director<S>>(&self, score_director: &mut D, (): Self::Undo) {
183        self.do_move(score_director);
184    }
185
186    fn descriptor_index(&self) -> usize {
187        self.descriptor_index
188    }
189
190    fn entity_indices(&self) -> &[usize] {
191        std::slice::from_ref(&self.entity_index)
192    }
193
194    fn variable_name(&self) -> &str {
195        self.variable_name
196    }
197
198    fn telemetry_label(&self) -> &'static str {
199        "list_reverse"
200    }
201
202    fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> Cow<'_, MoveTabuSignature> {
203        Cow::Owned(reverse_tabu_signature(
204            &self.access(),
205            self.coordinates(),
206            score_director,
207        ))
208    }
209
210    fn candidate_trace_identity(&self) -> Option<crate::stats::CandidateTraceIdentity> {
211        Some(reverse_candidate_trace_identity(
212            &self.access(),
213            self.coordinates(),
214        ))
215    }
216}