Skip to main content

solverforge_solver/builder/selector/
coverage_repair.rs

1use crate::builder::context::CoverageGroupBinding;
2use crate::phase::construction::coverage::{
3    capacity_conflict_moves, required_coverage_moves, CoverageMoveOptions,
4};
5
6const DEFAULT_MAX_MOVES_PER_STEP: usize = 256;
7
8pub struct CoverageRepairSelector<S> {
9    group: CoverageGroupBinding<S>,
10    value_candidate_limit: Option<usize>,
11    max_moves_per_step: Option<usize>,
12    require_hard_improvement: bool,
13}
14
15impl<S> CoverageRepairSelector<S> {
16    pub fn new(
17        group: CoverageGroupBinding<S>,
18        value_candidate_limit: Option<usize>,
19        max_moves_per_step: Option<usize>,
20        require_hard_improvement: bool,
21    ) -> Self {
22        Self {
23            group,
24            value_candidate_limit,
25            max_moves_per_step,
26            require_hard_improvement,
27        }
28    }
29
30    fn max_moves_per_step(&self) -> usize {
31        self.max_moves_per_step
32            .or(self.group.limits.max_moves_per_step)
33            .unwrap_or(DEFAULT_MAX_MOVES_PER_STEP)
34    }
35}
36
37impl<S> std::fmt::Debug for CoverageRepairSelector<S> {
38    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39        f.debug_struct("CoverageRepairSelector")
40            .field("group_name", &self.group.group_name)
41            .field("value_candidate_limit", &self.value_candidate_limit)
42            .field("max_moves_per_step", &self.max_moves_per_step())
43            .field("require_hard_improvement", &self.require_hard_improvement)
44            .finish()
45    }
46}
47
48pub struct CoverageRepairCursor<S>
49where
50    S: PlanningSolution + 'static,
51{
52    store: CandidateStore<S, ScalarMoveUnion<S, usize>>,
53    next_index: usize,
54}
55
56impl<S> CoverageRepairCursor<S>
57where
58    S: PlanningSolution + 'static,
59{
60    fn new(store: CandidateStore<S, ScalarMoveUnion<S, usize>>) -> Self {
61        Self {
62            store,
63            next_index: 0,
64        }
65    }
66}
67
68impl<S> MoveCursor<S, ScalarMoveUnion<S, usize>> for CoverageRepairCursor<S>
69where
70    S: PlanningSolution + 'static,
71{
72    fn next_candidate(&mut self) -> Option<CandidateId> {
73        if self.next_index >= self.store.len() {
74            return None;
75        }
76        let id = CandidateId::new(self.next_index);
77        self.next_index += 1;
78        Some(id)
79    }
80
81    fn candidate(
82        &self,
83        id: CandidateId,
84    ) -> Option<MoveCandidateRef<'_, S, ScalarMoveUnion<S, usize>>> {
85        self.store.candidate(id)
86    }
87
88    fn take_candidate(&mut self, id: CandidateId) -> ScalarMoveUnion<S, usize> {
89        self.store.take_candidate(id)
90    }
91}
92
93impl<S> MoveSelector<S, ScalarMoveUnion<S, usize>> for CoverageRepairSelector<S>
94where
95    S: PlanningSolution + 'static,
96{
97    type Cursor<'a>
98        = CoverageRepairCursor<S>
99    where
100        Self: 'a;
101
102    fn open_cursor<'a, D: solverforge_scoring::Director<S>>(
103        &'a self,
104        score_director: &D,
105    ) -> Self::Cursor<'a> {
106        let solution = score_director.working_solution();
107        let max_moves_per_step = self.max_moves_per_step();
108        let options = CoverageMoveOptions::for_repair(
109            &self.group,
110            self.value_candidate_limit,
111            max_moves_per_step,
112        );
113        let mut store = CandidateStore::with_capacity(max_moves_per_step);
114        for mov in required_coverage_moves(&self.group, solution, options)
115            .into_iter()
116            .chain(capacity_conflict_moves(&self.group, solution, options))
117        {
118            if store.len() >= max_moves_per_step {
119                break;
120            }
121            let mov = mov.with_require_hard_improvement(self.require_hard_improvement);
122            if mov.is_doable(score_director) {
123                store.push(ScalarMoveUnion::CompoundScalar(mov));
124            }
125        }
126        CoverageRepairCursor::new(store)
127    }
128
129    fn size<D: solverforge_scoring::Director<S>>(&self, _score_director: &D) -> usize {
130        self.max_moves_per_step()
131    }
132}