Skip to main content

solverforge_solver/heuristic/selector/
list_precedence.rs

1//! Public static facade for the canonical critical-path precedence kernel.
2
3use std::fmt::Debug;
4
5use solverforge_core::domain::PlanningSolution;
6use solverforge_scoring::Director;
7
8use crate::heuristic::r#move::ListMoveUnion;
9use crate::heuristic::selector::list_kernel::{
10    critical_analysis, filtered_move_count, filtered_multi_support_swap_count,
11    multi_critical_ruin_count, CriticalAnalysis, NativePrecedenceEmitter, PrecedenceCursor,
12};
13
14use super::entity::EntitySelector;
15use super::move_selector::{
16    CandidateId, MoveCandidateRef, MoveCursor, MoveSelector, MoveStreamContext,
17};
18use super::precedence_route::PrecedenceRouteHooks;
19
20/// Generates move families around critical same-list precedence arcs.
21///
22/// The public constructor and static move union are unchanged. Critical-path
23/// analysis, ordering, cycle pruning, candidate ownership, and mixed move
24/// emission are shared with the future runtime-list leaf.
25pub struct ListPrecedenceMoveSelector<S, V, ES> {
26    entity_selector: ES,
27    element_count: fn(&S) -> usize,
28    index_to_element: fn(&S, usize) -> V,
29    node_duration: fn(&S, V) -> usize,
30    fixed_successors: fn(&S, V, &mut Vec<V>),
31    entity_count: fn(&S) -> usize,
32    list_len: fn(&S, usize) -> usize,
33    list_get: fn(&S, usize, usize) -> Option<V>,
34    list_remove: fn(&mut S, usize, usize) -> Option<V>,
35    list_insert: fn(&mut S, usize, usize, V),
36    list_set: fn(&mut S, usize, usize, V),
37    list_reverse: fn(&mut S, usize, usize, usize),
38    ruin_remove: fn(&mut S, usize, usize) -> V,
39    ruin_insert: fn(&mut S, usize, usize, V),
40    element_owner_fn: Option<fn(&S, &V) -> Option<usize>>,
41    sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
42    sublist_insert: fn(&mut S, usize, usize, Vec<V>),
43    variable_name: &'static str,
44    descriptor_index: usize,
45}
46
47/// Public cursor facade over the canonical precedence stream.
48pub struct ListPrecedenceMoveCursor<S, V>
49where
50    S: PlanningSolution,
51    V: Clone + PartialEq + Send + Sync + Debug + 'static,
52{
53    inner: PrecedenceCursor<S, NativePrecedenceEmitter<S, V>>,
54}
55
56impl<S, V> ListPrecedenceMoveCursor<S, V>
57where
58    S: PlanningSolution,
59    V: Clone + PartialEq + Send + Sync + Debug + 'static,
60{
61    fn new(inner: PrecedenceCursor<S, NativePrecedenceEmitter<S, V>>) -> Self {
62        Self { inner }
63    }
64}
65
66impl<S, V> MoveCursor<S, ListMoveUnion<S, V>> for ListPrecedenceMoveCursor<S, V>
67where
68    S: PlanningSolution,
69    V: Clone + PartialEq + Send + Sync + Debug + 'static,
70{
71    fn next_candidate(&mut self) -> Option<CandidateId> {
72        self.inner.next_candidate()
73    }
74
75    fn candidate(&self, id: CandidateId) -> Option<MoveCandidateRef<'_, S, ListMoveUnion<S, V>>> {
76        self.inner.candidate(id)
77    }
78
79    fn take_candidate(&mut self, id: CandidateId) -> ListMoveUnion<S, V> {
80        self.inner.take_candidate(id)
81    }
82
83    fn next_owned_candidate(&mut self) -> Option<ListMoveUnion<S, V>> {
84        self.inner.next_owned_candidate()
85    }
86
87    fn next_owned_candidate_matching(
88        &mut self,
89        predicate: for<'a> fn(MoveCandidateRef<'a, S, ListMoveUnion<S, V>>) -> bool,
90    ) -> Option<ListMoveUnion<S, V>> {
91        self.inner.next_owned_candidate_matching(predicate)
92    }
93
94    fn release_candidate(&mut self, id: CandidateId) -> bool {
95        self.inner.release_candidate(id)
96    }
97}
98
99impl<S, V> Iterator for ListPrecedenceMoveCursor<S, V>
100where
101    S: PlanningSolution,
102    V: Clone + PartialEq + Send + Sync + Debug + 'static,
103{
104    type Item = ListMoveUnion<S, V>;
105
106    fn next(&mut self) -> Option<Self::Item> {
107        self.next_owned_candidate()
108    }
109}
110
111impl<S, V: Debug, ES: Debug> Debug for ListPrecedenceMoveSelector<S, V, ES> {
112    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113        formatter
114            .debug_struct("ListPrecedenceMoveSelector")
115            .field("entity_selector", &self.entity_selector)
116            .field("variable_name", &self.variable_name)
117            .finish()
118    }
119}
120
121impl<S, V, ES> ListPrecedenceMoveSelector<S, V, ES> {
122    #[allow(clippy::too_many_arguments)]
123    pub fn new(
124        entity_selector: ES,
125        element_count: fn(&S) -> usize,
126        index_to_element: fn(&S, usize) -> V,
127        node_duration: fn(&S, V) -> usize,
128        fixed_successors: fn(&S, V, &mut Vec<V>),
129        entity_count: fn(&S) -> usize,
130        list_len: fn(&S, usize) -> usize,
131        list_get: fn(&S, usize, usize) -> Option<V>,
132        list_remove: fn(&mut S, usize, usize) -> Option<V>,
133        list_insert: fn(&mut S, usize, usize, V),
134        list_set: fn(&mut S, usize, usize, V),
135        list_reverse: fn(&mut S, usize, usize, usize),
136        ruin_remove: fn(&mut S, usize, usize) -> V,
137        ruin_insert: fn(&mut S, usize, usize, V),
138        element_owner_fn: Option<fn(&S, &V) -> Option<usize>>,
139        sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
140        sublist_insert: fn(&mut S, usize, usize, Vec<V>),
141        variable_name: &'static str,
142        descriptor_index: usize,
143    ) -> Self {
144        Self {
145            entity_selector,
146            element_count,
147            index_to_element,
148            node_duration,
149            fixed_successors,
150            entity_count,
151            list_len,
152            list_get,
153            list_remove,
154            list_insert,
155            list_set,
156            list_reverse,
157            ruin_remove,
158            ruin_insert,
159            element_owner_fn,
160            sublist_remove,
161            sublist_insert,
162            variable_name,
163            descriptor_index,
164        }
165    }
166}
167
168impl<S, V, ES> MoveSelector<S, ListMoveUnion<S, V>> for ListPrecedenceMoveSelector<S, V, ES>
169where
170    S: PlanningSolution,
171    V: Clone + PartialEq + Send + Sync + Debug + 'static,
172    ES: EntitySelector<S>,
173{
174    type Cursor<'a>
175        = ListPrecedenceMoveCursor<S, V>
176    where
177        Self: 'a;
178
179    fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a> {
180        self.open_cursor_with_context(score_director, MoveStreamContext::default())
181    }
182
183    fn open_cursor_with_context<'a, D: Director<S>>(
184        &'a self,
185        score_director: &D,
186        context: MoveStreamContext,
187    ) -> Self::Cursor<'a> {
188        let analysis = self.analysis(score_director);
189        let emitter = self.emitter();
190        ListPrecedenceMoveCursor::new(PrecedenceCursor::new(
191            analysis.blocks,
192            analysis.route_graph,
193            context,
194            emitter,
195            self.descriptor_index,
196        ))
197    }
198
199    fn size<D: Director<S>>(&self, score_director: &D) -> usize {
200        let analysis = self.analysis(score_director);
201        analysis
202            .blocks
203            .iter()
204            .copied()
205            .map(|block| filtered_move_count(block, &analysis.route_graph))
206            .sum::<usize>()
207            + filtered_multi_support_swap_count(&analysis.blocks, &analysis.route_graph)
208            + multi_critical_ruin_count(&analysis.blocks)
209    }
210}
211
212impl<S, V, ES> ListPrecedenceMoveSelector<S, V, ES>
213where
214    S: PlanningSolution,
215    V: Clone + PartialEq + Send + Sync + Debug + 'static,
216    ES: EntitySelector<S>,
217{
218    fn analysis<D: Director<S>>(&self, score_director: &D) -> CriticalAnalysis {
219        critical_analysis(
220            score_director,
221            &self.entity_selector,
222            self.element_count,
223            self.index_to_element,
224            self.node_duration,
225            self.list_len,
226            PrecedenceRouteHooks::new(
227                self.fixed_successors,
228                self.entity_count,
229                self.list_len,
230                self.list_get,
231            ),
232        )
233    }
234
235    fn emitter(&self) -> NativePrecedenceEmitter<S, V> {
236        NativePrecedenceEmitter::new(
237            self.element_count,
238            self.index_to_element,
239            self.fixed_successors,
240            self.entity_count,
241            self.list_len,
242            self.list_get,
243            self.list_remove,
244            self.list_insert,
245            self.list_set,
246            self.list_reverse,
247            self.ruin_remove,
248            self.ruin_insert,
249            self.element_owner_fn,
250            self.sublist_remove,
251            self.sublist_insert,
252            self.variable_name,
253            self.descriptor_index,
254        )
255    }
256}