Skip to main content

solverforge_solver/heuristic/move/
dynamic_scalar_change.rs

1use 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}