Skip to main content

solverforge_solver/heuristic/selector/move_selector/
change.rs

1/// A change move selector that generates `ChangeMove` instances.
2pub struct ChangeMoveSelector<S, V, ES, VS> {
3    entity_selector: ES,
4    value_selector: VS,
5    getter: fn(&S, usize, usize) -> Option<V>,
6    setter: fn(&mut S, usize, usize, Option<V>),
7    descriptor_index: usize,
8    variable_index: usize,
9    variable_name: &'static str,
10    allows_unassigned: bool,
11    _phantom: PhantomData<(fn() -> S, fn() -> V)>,
12}
13
14impl<S, V: Debug, ES: Debug, VS: Debug> Debug for ChangeMoveSelector<S, V, ES, VS> {
15    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16        f.debug_struct("ChangeMoveSelector")
17            .field("entity_selector", &self.entity_selector)
18            .field("value_selector", &self.value_selector)
19            .field("descriptor_index", &self.descriptor_index)
20            .field("variable_index", &self.variable_index)
21            .field("variable_name", &self.variable_name)
22            .field("allows_unassigned", &self.allows_unassigned)
23            .finish()
24    }
25}
26
27impl<S: PlanningSolution, V: Clone, ES, VS> ChangeMoveSelector<S, V, ES, VS> {
28    pub fn new(
29        entity_selector: ES,
30        value_selector: VS,
31        getter: fn(&S, usize, usize) -> Option<V>,
32        setter: fn(&mut S, usize, usize, Option<V>),
33        descriptor_index: usize,
34        variable_index: usize,
35        variable_name: &'static str,
36    ) -> Self {
37        Self {
38            entity_selector,
39            value_selector,
40            getter,
41            setter,
42            descriptor_index,
43            variable_index,
44            variable_name,
45            allows_unassigned: false,
46            _phantom: PhantomData,
47        }
48    }
49
50    pub fn with_allows_unassigned(mut self, allows_unassigned: bool) -> Self {
51        self.allows_unassigned = allows_unassigned;
52        self
53    }
54}
55
56impl<S: PlanningSolution, V: Clone + Send + Sync + Debug + 'static>
57    ChangeMoveSelector<S, V, FromSolutionEntitySelector, StaticValueSelector<S, V>>
58{
59    pub fn simple(
60        getter: fn(&S, usize, usize) -> Option<V>,
61        setter: fn(&mut S, usize, usize, Option<V>),
62        descriptor_index: usize,
63        variable_index: usize,
64        variable_name: &'static str,
65        values: Vec<V>,
66    ) -> Self {
67        Self {
68            entity_selector: FromSolutionEntitySelector::new(descriptor_index),
69            value_selector: StaticValueSelector::new(values),
70            getter,
71            setter,
72            descriptor_index,
73            variable_index,
74            variable_name,
75            allows_unassigned: false,
76            _phantom: PhantomData,
77        }
78    }
79}
80
81impl<S, V, ES, VS> MoveSelector<S, ChangeMove<S, V>> for ChangeMoveSelector<S, V, ES, VS>
82where
83    S: PlanningSolution,
84    V: Clone + PartialEq + Send + Sync + Debug + 'static,
85    ES: EntitySelector<S>,
86    VS: ValueSelector<S, V>,
87{
88    type Cursor<'a>
89        = ArenaMoveCursor<S, ChangeMove<S, V>>
90    where
91        Self: 'a;
92
93    fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a> {
94        let descriptor_index = self.descriptor_index;
95        let variable_index = self.variable_index;
96        let variable_name = self.variable_name;
97        let getter = self.getter;
98        let setter = self.setter;
99        let allows_unassigned = self.allows_unassigned;
100        let value_selector = &self.value_selector;
101        let solution = score_director.working_solution();
102        let entity_values: Vec<_> = self
103            .entity_selector
104            .iter(score_director)
105            .map(|entity_ref| {
106                let current_assigned =
107                    getter(solution, entity_ref.entity_index, variable_index).is_some();
108                let values = value_selector.iter(
109                    score_director,
110                    entity_ref.descriptor_index,
111                    entity_ref.entity_index,
112                );
113                (entity_ref, values, current_assigned)
114            })
115            .collect();
116
117        let iter =
118            entity_values
119                .into_iter()
120                .flat_map(move |(entity_ref, values, current_assigned)| {
121                    let to_none = (allows_unassigned && current_assigned).then(|| {
122                        ChangeMove::new(
123                            entity_ref.entity_index,
124                            None,
125                            getter,
126                            setter,
127                            variable_index,
128                            variable_name,
129                            descriptor_index,
130                        )
131                    });
132                    values
133                        .map(move |value| {
134                            ChangeMove::new(
135                                entity_ref.entity_index,
136                                Some(value),
137                                getter,
138                                setter,
139                                variable_index,
140                                variable_name,
141                                descriptor_index,
142                            )
143                        })
144                        .chain(to_none)
145                });
146        ArenaMoveCursor::from_moves(iter)
147    }
148
149    fn size<D: Director<S>>(&self, score_director: &D) -> usize {
150        self.entity_selector
151            .iter(score_director)
152            .map(|entity_ref| {
153                self.value_selector.size(
154                    score_director,
155                    entity_ref.descriptor_index,
156                    entity_ref.entity_index,
157                ) + usize::from(
158                    self.allows_unassigned
159                        && (self.getter)(
160                            score_director.working_solution(),
161                            entity_ref.entity_index,
162                            self.variable_index,
163                        )
164                        .is_some(),
165                )
166            })
167            .sum()
168    }
169}
170