solverforge_solver/heuristic/selector/k_opt/
selector.rs1use std::fmt::Debug;
4use std::marker::PhantomData;
5
6use solverforge_core::domain::PlanningSolution;
7use solverforge_scoring::Director;
8
9use crate::heuristic::r#move::k_opt_reconnection::KOptReconnection;
10use crate::heuristic::r#move::k_opt_reconnection::{
11 enumerate_reconnections, THREE_OPT_RECONNECTIONS,
12};
13use crate::heuristic::r#move::KOptMove;
14
15use super::super::entity::EntitySelector;
16use super::super::typed_move_selector::MoveSelector;
17use super::config::KOptConfig;
18use super::iterators::count_cut_combinations;
19use super::iterators::CutCombinationIterator;
20
21pub struct KOptMoveSelector<S, V, ES> {
26 entity_selector: ES,
28 config: KOptConfig,
30 owned_patterns: Vec<KOptReconnection>,
32 list_len: fn(&S, usize) -> usize,
33 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
35 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
37 variable_name: &'static str,
39 descriptor_index: usize,
41 _phantom: PhantomData<(fn() -> S, fn() -> V)>,
42}
43
44impl<S, V: Debug, ES: Debug> Debug for KOptMoveSelector<S, V, ES> {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 f.debug_struct("KOptMoveSelector")
47 .field("entity_selector", &self.entity_selector)
48 .field("config", &self.config)
49 .field("pattern_count", &self.owned_patterns.len())
50 .field("variable_name", &self.variable_name)
51 .finish()
52 }
53}
54
55impl<S: PlanningSolution, V, ES> KOptMoveSelector<S, V, ES> {
56 #[allow(clippy::too_many_arguments)]
57 pub fn new(
58 entity_selector: ES,
59 config: KOptConfig,
60 list_len: fn(&S, usize) -> usize,
61 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
62 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
63 variable_name: &'static str,
64 descriptor_index: usize,
65 ) -> Self {
66 let owned_patterns: Vec<KOptReconnection> = if config.k == 3 {
69 THREE_OPT_RECONNECTIONS.to_vec()
70 } else {
71 enumerate_reconnections(config.k)
72 };
73
74 Self {
75 entity_selector,
76 config,
77 owned_patterns,
78 list_len,
79 sublist_remove,
80 sublist_insert,
81 variable_name,
82 descriptor_index,
83 _phantom: PhantomData,
84 }
85 }
86}
87
88impl<S, V, ES> MoveSelector<S, KOptMove<S, V>> for KOptMoveSelector<S, V, ES>
89where
90 S: PlanningSolution,
91 ES: EntitySelector<S>,
92 V: Clone + Send + Sync + Debug + 'static,
93{
94 fn iter_moves<'a, D: Director<S>>(
95 &'a self,
96 score_director: &'a D,
97 ) -> impl Iterator<Item = KOptMove<S, V>> + 'a {
98 let k = self.config.k;
99 let min_seg = self.config.min_segment_len;
100 let patterns = &self.owned_patterns;
101 let list_len = self.list_len;
102 let sublist_remove = self.sublist_remove;
103 let sublist_insert = self.sublist_insert;
104 let variable_name = self.variable_name;
105 let descriptor_index = self.descriptor_index;
106
107 self.entity_selector
108 .iter(score_director)
109 .flat_map(move |entity_ref| {
110 let entity_idx = entity_ref.entity_index;
111 let solution = score_director.working_solution();
112 let len = list_len(solution, entity_idx);
113
114 let cuts_iter = CutCombinationIterator::new(k, len, min_seg, entity_idx);
116
117 cuts_iter.flat_map(move |cuts| {
118 patterns.iter().map(move |pattern| {
120 KOptMove::new(
121 &cuts,
122 pattern,
123 list_len,
124 sublist_remove,
125 sublist_insert,
126 variable_name,
127 descriptor_index,
128 )
129 })
130 })
131 })
132 }
133
134 fn size<D: Director<S>>(&self, score_director: &D) -> usize {
135 let k = self.config.k;
136 let min_seg = self.config.min_segment_len;
137 let pattern_count = self.owned_patterns.len();
138
139 self.entity_selector
140 .iter(score_director)
141 .map(|entity_ref| {
142 let solution = score_director.working_solution();
143 let len = (self.list_len)(solution, entity_ref.entity_index);
144 count_cut_combinations(k, len, min_seg) * pattern_count
145 })
146 .sum()
147 }
148}