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::move_selector::{ArenaMoveCursor, 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 list_get: fn(&S, usize, usize) -> Option<V>,
34 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
36 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
38 variable_name: &'static str,
40 descriptor_index: usize,
42 _phantom: PhantomData<(fn() -> S, fn() -> V)>,
43}
44
45impl<S, V: Debug, ES: Debug> Debug for KOptMoveSelector<S, V, ES> {
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 f.debug_struct("KOptMoveSelector")
48 .field("entity_selector", &self.entity_selector)
49 .field("config", &self.config)
50 .field("pattern_count", &self.owned_patterns.len())
51 .field("variable_name", &self.variable_name)
52 .finish()
53 }
54}
55
56impl<S: PlanningSolution, V, ES> KOptMoveSelector<S, V, ES> {
57 #[allow(clippy::too_many_arguments)]
58 pub fn new(
59 entity_selector: ES,
60 config: KOptConfig,
61 list_len: fn(&S, usize) -> usize,
62 list_get: fn(&S, usize, usize) -> Option<V>,
63 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
64 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
65 variable_name: &'static str,
66 descriptor_index: usize,
67 ) -> Self {
68 let owned_patterns: Vec<KOptReconnection> = if config.k == 3 {
71 THREE_OPT_RECONNECTIONS.to_vec()
72 } else {
73 enumerate_reconnections(config.k)
74 };
75
76 Self {
77 entity_selector,
78 config,
79 owned_patterns,
80 list_len,
81 list_get,
82 sublist_remove,
83 sublist_insert,
84 variable_name,
85 descriptor_index,
86 _phantom: PhantomData,
87 }
88 }
89}
90
91impl<S, V, ES> MoveSelector<S, KOptMove<S, V>> for KOptMoveSelector<S, V, ES>
92where
93 S: PlanningSolution,
94 ES: EntitySelector<S>,
95 V: Clone + Send + Sync + Debug + 'static,
96{
97 type Cursor<'a>
98 = ArenaMoveCursor<S, KOptMove<S, V>>
99 where
100 Self: 'a;
101
102 fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a> {
103 let k = self.config.k;
104 let min_seg = self.config.min_segment_len;
105 let patterns = &self.owned_patterns;
106 let list_len = self.list_len;
107 let list_get = self.list_get;
108 let sublist_remove = self.sublist_remove;
109 let sublist_insert = self.sublist_insert;
110 let variable_name = self.variable_name;
111 let descriptor_index = self.descriptor_index;
112 let entity_lens: Vec<_> = self
113 .entity_selector
114 .iter(score_director)
115 .map(|entity_ref| {
116 let entity_idx = entity_ref.entity_index;
117 let len = list_len(score_director.working_solution(), entity_idx);
118 (entity_idx, len)
119 })
120 .collect();
121
122 ArenaMoveCursor::from_moves(entity_lens.into_iter().flat_map(move |(entity_idx, len)| {
123 let cuts_iter = CutCombinationIterator::new(k, len, min_seg, entity_idx);
124
125 cuts_iter.flat_map(move |cuts| {
126 patterns.iter().map(move |pattern| {
127 KOptMove::new(
128 &cuts,
129 pattern,
130 list_len,
131 list_get,
132 sublist_remove,
133 sublist_insert,
134 variable_name,
135 descriptor_index,
136 )
137 })
138 })
139 }))
140 }
141
142 fn size<D: Director<S>>(&self, score_director: &D) -> usize {
143 let k = self.config.k;
144 let min_seg = self.config.min_segment_len;
145 let pattern_count = self.owned_patterns.len();
146
147 self.entity_selector
148 .iter(score_director)
149 .map(|entity_ref| {
150 let solution = score_director.working_solution();
151 let len = (self.list_len)(solution, entity_ref.entity_index);
152 count_cut_combinations(k, len, min_seg) * pattern_count
153 })
154 .sum()
155 }
156}