Skip to main content

solverforge_solver/builder/
context.rs

1//! Context types that carry domain function pointers into the builder layer.
2
3use std::marker::PhantomData;
4
5use crate::heuristic::selector::k_opt::ListPositionDistanceMeter;
6use crate::heuristic::selector::nearby_list_change::CrossEntityDistanceMeter;
7
8/// Adapts a `CrossEntityDistanceMeter` to `ListPositionDistanceMeter` for intra-entity use.
9///
10/// `NearbyKOptMoveSelector` requires a `ListPositionDistanceMeter` (4-param intra-entity),
11/// but `ListContext.intra_distance_meter` is a `CrossEntityDistanceMeter` (5-param).
12/// This adapter bridges the two by always calling with `src_entity_idx == dst_entity_idx`.
13#[derive(Debug, Clone)]
14pub struct IntraDistanceAdapter<T>(pub T);
15
16impl<S, T: CrossEntityDistanceMeter<S>> ListPositionDistanceMeter<S> for IntraDistanceAdapter<T> {
17    fn distance(&self, solution: &S, entity_idx: usize, pos_a: usize, pos_b: usize) -> f64 {
18        self.0
19            .distance(solution, entity_idx, pos_a, entity_idx, pos_b)
20    }
21}
22
23/// Function-pointer context for basic (non-list) variable solvers.
24///
25/// Carries all domain callbacks needed to construct move selectors
26/// without requiring `dyn` or closures.
27pub struct BasicContext<S> {
28    /// Gets the planning variable for entity `i`.
29    pub get_variable: fn(&S, usize) -> Option<usize>,
30    /// Sets the planning variable for entity `i`.
31    pub set_variable: fn(&mut S, usize, Option<usize>),
32    /// All valid values for the variable.
33    pub values: Vec<usize>,
34    /// Descriptor index for the entity collection.
35    pub descriptor_index: usize,
36    /// Variable field name.
37    pub variable_field: &'static str,
38}
39
40impl<S> std::fmt::Debug for BasicContext<S> {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        f.debug_struct("BasicContext")
43            .field("descriptor_index", &self.descriptor_index)
44            .field("variable_field", &self.variable_field)
45            .field("values_len", &self.values.len())
46            .finish()
47    }
48}
49
50/// Function-pointer context for list variable solvers.
51///
52/// Carries all domain callbacks and distance meters needed to construct
53/// list move selectors without requiring `dyn` or closures.
54pub struct ListContext<S, V, DM, IDM> {
55    /// Returns the length of the list for entity `i`.
56    pub list_len: fn(&S, usize) -> usize,
57    /// Removes element at `pos` from entity `i`, returning it (returns `None` if out of bounds).
58    pub list_remove: fn(&mut S, usize, usize) -> Option<V>,
59    /// Inserts `val` at `pos` in entity `i`.
60    pub list_insert: fn(&mut S, usize, usize, V),
61    /// Gets element at `pos` in entity `i`.
62    pub list_get: fn(&S, usize, usize) -> Option<V>,
63    /// Replaces element at `pos` in entity `i`.
64    pub list_set: fn(&mut S, usize, usize, V),
65    /// Reverses the segment `[start, end)` in entity `i`.
66    pub list_reverse: fn(&mut S, usize, usize, usize),
67    /// Removes segment `[start, end)` from entity `i`.
68    pub sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
69    /// Inserts `items` at `pos` in entity `i`.
70    pub sublist_insert: fn(&mut S, usize, usize, Vec<V>),
71    /// Removes element at `pos` from entity `i` for ruin moves (panics if out of bounds).
72    pub ruin_remove: fn(&mut S, usize, usize) -> V,
73    /// Inserts `val` at `pos` in entity `i` for ruin reinsertion.
74    pub ruin_insert: fn(&mut S, usize, usize, V),
75    /// Returns the total number of list owner entities.
76    pub entity_count: fn(&S) -> usize,
77    /// Cross-entity (inter-route) distance meter.
78    pub cross_distance_meter: DM,
79    /// Intra-entity (intra-route) distance meter.
80    pub intra_distance_meter: IDM,
81    /// List variable field name.
82    pub variable_name: &'static str,
83    /// Descriptor index for the list owner entity collection.
84    pub descriptor_index: usize,
85    _phantom: PhantomData<(fn() -> S, fn() -> V)>,
86}
87
88impl<S, V, DM, IDM> ListContext<S, V, DM, IDM> {
89    /// Creates a new list context.
90    #[allow(clippy::too_many_arguments)]
91    pub fn new(
92        list_len: fn(&S, usize) -> usize,
93        list_remove: fn(&mut S, usize, usize) -> Option<V>,
94        list_insert: fn(&mut S, usize, usize, V),
95        list_get: fn(&S, usize, usize) -> Option<V>,
96        list_set: fn(&mut S, usize, usize, V),
97        list_reverse: fn(&mut S, usize, usize, usize),
98        sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
99        sublist_insert: fn(&mut S, usize, usize, Vec<V>),
100        ruin_remove: fn(&mut S, usize, usize) -> V,
101        ruin_insert: fn(&mut S, usize, usize, V),
102        entity_count: fn(&S) -> usize,
103        cross_distance_meter: DM,
104        intra_distance_meter: IDM,
105        variable_name: &'static str,
106        descriptor_index: usize,
107    ) -> Self {
108        Self {
109            list_len,
110            list_remove,
111            list_insert,
112            list_get,
113            list_set,
114            list_reverse,
115            sublist_remove,
116            sublist_insert,
117            ruin_remove,
118            ruin_insert,
119            entity_count,
120            cross_distance_meter,
121            intra_distance_meter,
122            variable_name,
123            descriptor_index,
124            _phantom: PhantomData,
125        }
126    }
127}
128
129impl<S, V, DM: std::fmt::Debug, IDM: std::fmt::Debug> std::fmt::Debug
130    for ListContext<S, V, DM, IDM>
131{
132    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133        f.debug_struct("ListContext")
134            .field("variable_name", &self.variable_name)
135            .field("descriptor_index", &self.descriptor_index)
136            .finish()
137    }
138}