Skip to main content

solverforge_solver/heuristic/selector/
dynamic_scalar_change.rs

1//! Public dynamic scalar change facade over the canonical scalar leaf.
2
3use std::fmt::{self, Debug};
4
5use solverforge_core::domain::{DynamicScalarVariableSlot, PlanningSolution};
6use solverforge_core::score::Score;
7use solverforge_scoring::Director;
8
9use crate::heuristic::r#move::{DynamicScalarChangeMove, MoveArena};
10
11use super::move_selector::{MoveSelector, MoveStreamContext};
12use super::scalar_neighborhood::{
13    dynamic_slot, emit_dynamic_scalar_change_move, RuntimeScalarFacadeCursor,
14    ScalarNeighborhoodLeaf, ScalarNeighborhoodSpec,
15};
16
17/// Direct public facade. Candidate discovery, ordering, and ownership live in
18/// `ScalarNeighborhoodLeaf`; this type preserves the established public move
19/// payload only.
20pub struct DynamicScalarChangeMoveSelector<S> {
21    leaf: ScalarNeighborhoodLeaf<S>,
22}
23
24pub type DynamicScalarChangeMoveCursor<S> =
25    RuntimeScalarFacadeCursor<S, DynamicScalarChangeMove<S>>;
26
27impl<S> DynamicScalarChangeMoveSelector<S>
28where
29    S: PlanningSolution,
30{
31    pub fn new(slot: DynamicScalarVariableSlot<S>, value_candidate_limit: Option<usize>) -> Self {
32        let leaf = ScalarNeighborhoodLeaf::from_spec(
33            ScalarNeighborhoodSpec::Change {
34                value_candidate_limit,
35            },
36            dynamic_slot(slot),
37            None,
38        )
39        .expect("ordinary dynamic scalar slots always provide a candidate stream");
40        Self { leaf }
41    }
42}
43
44impl<S> Debug for DynamicScalarChangeMoveSelector<S>
45where
46    S: PlanningSolution,
47{
48    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
49        formatter
50            .debug_struct("DynamicScalarChangeMoveSelector")
51            .field("leaf", &self.leaf)
52            .finish()
53    }
54}
55
56impl<S> MoveSelector<S, DynamicScalarChangeMove<S>> for DynamicScalarChangeMoveSelector<S>
57where
58    S: PlanningSolution,
59    S::Score: Score,
60{
61    type Cursor<'a>
62        = DynamicScalarChangeMoveCursor<S>
63    where
64        Self: 'a;
65
66    fn open_cursor<'a, D: Director<S>>(&'a self, director: &D) -> Self::Cursor<'a> {
67        self.open_cursor_with_context(director, MoveStreamContext::default())
68    }
69
70    fn open_cursor_with_context<'a, D: Director<S>>(
71        &'a self,
72        director: &D,
73        context: MoveStreamContext,
74    ) -> Self::Cursor<'a> {
75        let mut stream_state = self.leaf.new_stream_state();
76        RuntimeScalarFacadeCursor::new(
77            self.leaf
78                .open_cursor_with_stream_state(&mut stream_state, director, context),
79            emit_dynamic_scalar_change_move::<S>,
80        )
81    }
82
83    fn size<D: Director<S>>(&self, director: &D) -> usize {
84        self.open_cursor(director).count()
85    }
86
87    fn append_moves<D: Director<S>>(
88        &self,
89        director: &D,
90        arena: &mut MoveArena<DynamicScalarChangeMove<S>>,
91    ) {
92        arena.extend(self.open_cursor(director));
93    }
94}