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