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