Skip to main content

solverforge_solver/scope/
step.rs

1// Step-level scope.
2
3use solverforge_core::domain::PlanningSolution;
4use solverforge_scoring::Director;
5
6use crate::heuristic::r#move::Move;
7
8use super::solver::{PendingControl, ProgressCallback};
9use super::{PhaseScope, SolverScope};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub(crate) enum StepControlPolicy {
13    ObserveConfigLimits,
14    CompleteMandatoryConstruction,
15}
16
17impl StepControlPolicy {
18    pub(crate) fn for_required_construction(required_only: bool) -> Self {
19        if required_only {
20            Self::CompleteMandatoryConstruction
21        } else {
22            Self::ObserveConfigLimits
23        }
24    }
25
26    pub(crate) fn should_terminate_construction<S, D, ProgressCb>(
27        self,
28        solver_scope: &mut SolverScope<'_, S, D, ProgressCb>,
29    ) -> bool
30    where
31        S: PlanningSolution,
32        D: Director<S>,
33        ProgressCb: ProgressCallback<S>,
34    {
35        match self {
36            Self::ObserveConfigLimits => solver_scope.should_terminate_construction(),
37            Self::CompleteMandatoryConstruction => {
38                solver_scope.should_interrupt_mandatory_construction()
39            }
40        }
41    }
42}
43
44/// Scope for a single step within a phase.
45///
46/// # Type Parameters
47/// * `'t` - Lifetime of the termination flag
48/// * `'a` - Lifetime of the phase scope reference
49/// * `'b` - Lifetime of the solver scope reference
50/// * `S` - The planning solution type
51/// * `D` - The score director type
52/// * `BestCb` - The best-solution callback type
53pub struct StepScope<'t, 'a, 'b, S: PlanningSolution, D: Director<S>, BestCb = ()> {
54    // Reference to the parent phase scope.
55    phase_scope: &'a mut PhaseScope<'t, 'b, S, D, BestCb>,
56    // Index of this step within the phase (0-based).
57    step_index: u64,
58    // Score after this step.
59    step_score: Option<S::Score>,
60    control_policy: StepControlPolicy,
61    control_polling_required: bool,
62    progress_polling_required: bool,
63}
64
65impl<'t, 'a, 'b, S: PlanningSolution, D: Director<S>, BestCb: ProgressCallback<S>>
66    StepScope<'t, 'a, 'b, S, D, BestCb>
67{
68    pub fn new(phase_scope: &'a mut PhaseScope<'t, 'b, S, D, BestCb>) -> Self {
69        Self::new_with_control_policy(phase_scope, StepControlPolicy::ObserveConfigLimits)
70    }
71
72    pub(crate) fn new_with_control_policy(
73        phase_scope: &'a mut PhaseScope<'t, 'b, S, D, BestCb>,
74        control_policy: StepControlPolicy,
75    ) -> Self {
76        let step_index = phase_scope.step_count();
77        let control_polling_required = match control_policy {
78            StepControlPolicy::ObserveConfigLimits => {
79                phase_scope.solver_scope().config_control_polling_required()
80            }
81            StepControlPolicy::CompleteMandatoryConstruction => phase_scope
82                .solver_scope()
83                .mandatory_control_polling_required(),
84        };
85        Self {
86            phase_scope,
87            step_index,
88            step_score: None,
89            control_policy,
90            control_polling_required,
91            progress_polling_required: BestCb::PUBLISHES_PROGRESS
92                || tracing::enabled!(tracing::Level::DEBUG),
93        }
94    }
95
96    pub fn step_index(&self) -> u64 {
97        self.step_index
98    }
99
100    pub fn step_score(&self) -> Option<&S::Score> {
101        self.step_score.as_ref()
102    }
103
104    pub(crate) fn control_policy(&self) -> StepControlPolicy {
105        self.control_policy
106    }
107
108    pub(crate) fn pending_control(&self) -> PendingControl {
109        if !self.control_polling_required {
110            return PendingControl::Continue;
111        }
112        match self.control_policy {
113            StepControlPolicy::ObserveConfigLimits => {
114                self.phase_scope.solver_scope().pending_control()
115            }
116            StepControlPolicy::CompleteMandatoryConstruction => self
117                .phase_scope
118                .solver_scope()
119                .mandatory_construction_pending_control(),
120        }
121    }
122
123    pub(crate) fn progress_polling_required(&self) -> bool {
124        self.progress_polling_required
125    }
126
127    pub fn set_step_score(&mut self, score: S::Score)
128    where
129        S::Score: Copy,
130    {
131        self.phase_scope.solver_scope_mut().set_current_score(score);
132        self.phase_scope
133            .solver_scope_mut()
134            .observe_phase_step_score(score);
135        self.step_score = Some(score);
136    }
137
138    /// Marks this step as complete and increments counters.
139    pub fn complete(&mut self) {
140        self.phase_scope.increment_step_count();
141        self.phase_scope.solver_scope_mut().pause_if_requested();
142        self.phase_scope.report_progress_if_due();
143    }
144
145    pub fn phase_scope(&self) -> &PhaseScope<'t, 'b, S, D, BestCb> {
146        self.phase_scope
147    }
148
149    pub fn phase_scope_mut(&mut self) -> &mut PhaseScope<'t, 'b, S, D, BestCb> {
150        self.phase_scope
151    }
152
153    pub fn score_director(&self) -> &D {
154        self.phase_scope.score_director()
155    }
156
157    pub(crate) fn score_director_mut(&mut self) -> &mut D {
158        self.phase_scope.score_director_mut()
159    }
160
161    pub fn mutate<T, F>(&mut self, mutate: F) -> T
162    where
163        F: FnOnce(&mut D) -> T,
164    {
165        self.phase_scope.mutate(mutate)
166    }
167
168    pub(crate) fn apply_committed_move<M>(&mut self, mov: &M)
169    where
170        M: Move<S>,
171    {
172        self.phase_scope
173            .solver_scope_mut()
174            .apply_committed_move(mov);
175    }
176
177    pub(crate) fn apply_committed_change<F>(&mut self, change: F)
178    where
179        F: FnOnce(&mut D),
180    {
181        self.phase_scope
182            .solver_scope_mut()
183            .apply_committed_change(change);
184    }
185
186    /// Calculates the current score.
187    pub fn calculate_score(&mut self) -> S::Score {
188        self.phase_scope.calculate_score()
189    }
190}