solverforge_solver/heuristic/move/
dynamic_scalar_change.rs1use std::fmt::Debug;
2
3use smallvec::smallvec;
4use solverforge_core::domain::{DynamicScalarVariableSlot, PlanningSolution};
5use solverforge_scoring::Director;
6
7use crate::stats::{CandidateTraceCoordinate, CandidateTraceIdentity};
8
9use super::metadata::{encode_option_debug, encode_usize, hash_str, MoveTabuScope};
10use super::{Move, MoveTabuSignature};
11
12pub struct DynamicScalarChangeMove<S> {
13 slot: DynamicScalarVariableSlot<S>,
14 entity_index: usize,
15 to_value: Option<usize>,
16}
17
18impl<S> Clone for DynamicScalarChangeMove<S> {
19 fn clone(&self) -> Self {
20 Self {
21 slot: self.slot.clone(),
22 entity_index: self.entity_index,
23 to_value: self.to_value,
24 }
25 }
26}
27
28impl<S> Debug for DynamicScalarChangeMove<S> {
29 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 f.debug_struct("DynamicScalarChangeMove")
31 .field("entity_index", &self.entity_index)
32 .field("descriptor_index", &self.slot.descriptor_index())
33 .field("variable", &self.slot.variable)
34 .field("variable_name", &self.slot.variable_name)
35 .field("to_value", &self.to_value)
36 .finish()
37 }
38}
39
40impl<S> DynamicScalarChangeMove<S> {
41 pub fn new(
42 slot: DynamicScalarVariableSlot<S>,
43 entity_index: usize,
44 to_value: Option<usize>,
45 ) -> Self {
46 Self {
47 slot,
48 entity_index,
49 to_value,
50 }
51 }
52
53 pub fn entity_index(&self) -> usize {
54 self.entity_index
55 }
56
57 pub fn to_value(&self) -> Option<usize> {
58 self.to_value
59 }
60}
61
62impl<S> Move<S> for DynamicScalarChangeMove<S>
63where
64 S: PlanningSolution,
65{
66 type Undo = Option<usize>;
67
68 fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
69 if !self.slot.value_is_legal(
70 score_director.working_solution(),
71 self.entity_index,
72 self.to_value,
73 ) {
74 return false;
75 }
76 self.slot
77 .current_value(score_director.working_solution(), self.entity_index)
78 != self.to_value
79 }
80
81 fn do_move<D: Director<S>>(&self, score_director: &mut D) -> Self::Undo {
82 let old_value = self
83 .slot
84 .current_value(score_director.working_solution(), self.entity_index);
85 let descriptor_index = self.slot.descriptor_index();
86
87 score_director.before_variable_changed(descriptor_index, self.entity_index);
88 self.slot.set_value(
89 score_director.working_solution_mut(),
90 self.entity_index,
91 self.to_value,
92 );
93 score_director.after_variable_changed(descriptor_index, self.entity_index);
94
95 old_value
96 }
97
98 fn undo_move<D: Director<S>>(&self, score_director: &mut D, undo: Self::Undo) {
99 let descriptor_index = self.slot.descriptor_index();
100 score_director.before_variable_changed(descriptor_index, self.entity_index);
101 self.slot.set_value(
102 score_director.working_solution_mut(),
103 self.entity_index,
104 undo,
105 );
106 score_director.after_variable_changed(descriptor_index, self.entity_index);
107 }
108
109 fn descriptor_index(&self) -> usize {
110 self.slot.descriptor_index()
111 }
112
113 fn entity_indices(&self) -> &[usize] {
114 std::slice::from_ref(&self.entity_index)
115 }
116
117 fn variable_name(&self) -> &str {
118 self.slot.variable_name
119 }
120
121 fn telemetry_label(&self) -> &'static str {
122 "dynamic_scalar_change"
123 }
124
125 fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> MoveTabuSignature {
126 let current = self
127 .slot
128 .current_value(score_director.working_solution(), self.entity_index);
129 let from_id = encode_option_debug(current.as_ref());
130 let to_id = encode_option_debug(self.to_value.as_ref());
131 let entity_id = encode_usize(self.entity_index);
132 let variable_id = hash_str(self.slot.variable_name);
133 let scope = MoveTabuScope::new(self.slot.descriptor_index(), self.slot.variable_name);
134
135 MoveTabuSignature::new(
136 scope,
137 smallvec![
138 encode_usize(self.slot.descriptor_index()),
139 variable_id,
140 entity_id,
141 from_id,
142 to_id
143 ],
144 smallvec![
145 encode_usize(self.slot.descriptor_index()),
146 variable_id,
147 entity_id,
148 to_id,
149 from_id
150 ],
151 )
152 .with_entity_tokens([scope.entity_token(entity_id)])
153 .with_destination_value_tokens([scope.value_token(to_id)])
154 }
155
156 fn candidate_trace_identity(&self) -> Option<CandidateTraceIdentity> {
157 Some(CandidateTraceIdentity::logical_move(
158 self.slot.descriptor_index(),
159 self.slot.variable_name,
160 "scalar_change",
161 vec![
162 CandidateTraceCoordinate::from(self.entity_index),
163 CandidateTraceCoordinate::from(self.to_value),
164 ],
165 ))
166 }
167}