Skip to main content

solverforge_solver/phase/construction/grouped_scalar/
assignment_candidate.rs

1#[cfg(test)]
2use solverforge_core::domain::PlanningSolution;
3
4#[cfg(test)]
5use super::assignment_stream::collect_assignment_moves;
6use crate::builder::ScalarAssignmentBinding;
7#[cfg(test)]
8use crate::heuristic::r#move::CompoundScalarMove;
9
10#[derive(Clone, Copy, Debug)]
11pub struct ScalarAssignmentMoveOptions {
12    pub(crate) value_candidate_limit: Option<usize>,
13    pub(crate) max_moves: usize,
14    pub(crate) max_depth: usize,
15    pub(crate) max_rematch_size: usize,
16    pub(crate) entity_offset: usize,
17    pub(crate) required_scarcity_ordering: bool,
18}
19
20impl ScalarAssignmentMoveOptions {
21    pub fn for_construction(limits: crate::builder::ScalarGroupLimits) -> Self {
22        Self {
23            value_candidate_limit: limits.value_candidate_limit,
24            max_moves: limits.group_candidate_limit.unwrap_or(usize::MAX),
25            max_depth: limits.max_augmenting_depth.unwrap_or(3),
26            max_rematch_size: limits.max_rematch_size.unwrap_or(4).max(2),
27            entity_offset: 0,
28            required_scarcity_ordering: true,
29        }
30    }
31
32    pub(crate) fn for_selector(
33        limits: crate::builder::ScalarGroupLimits,
34        value_candidate_limit: Option<usize>,
35        max_moves_per_step: usize,
36        entity_offset: usize,
37    ) -> Self {
38        Self {
39            value_candidate_limit: value_candidate_limit.or(limits.value_candidate_limit),
40            max_moves: max_moves_per_step,
41            max_depth: limits.max_augmenting_depth.unwrap_or(3),
42            max_rematch_size: limits.max_rematch_size.unwrap_or(4).max(2),
43            entity_offset,
44            required_scarcity_ordering: true,
45        }
46    }
47
48    pub fn with_max_moves(mut self, max_moves: usize) -> Self {
49        self.max_moves = max_moves;
50        self
51    }
52
53    pub fn with_entity_offset(mut self, entity_offset: usize) -> Self {
54        self.entity_offset = entity_offset;
55        self
56    }
57
58    pub fn with_required_scarcity_ordering(mut self, enabled: bool) -> Self {
59        self.required_scarcity_ordering = enabled;
60        self
61    }
62}
63
64#[derive(Clone, Copy)]
65pub(super) struct AssignmentMoveIntent {
66    pub(super) allow_optional_displacement: bool,
67    pub(super) reason: &'static str,
68}
69
70impl AssignmentMoveIntent {
71    pub(super) const fn required() -> Self {
72        Self {
73            allow_optional_displacement: true,
74            reason: "scalar_assignment_required",
75        }
76    }
77
78    pub(super) const fn optional() -> Self {
79        Self {
80            allow_optional_displacement: false,
81            reason: "scalar_assignment_optional",
82        }
83    }
84
85    pub(super) const fn capacity_repair() -> Self {
86        Self {
87            allow_optional_displacement: true,
88            reason: "scalar_assignment_capacity_repair",
89        }
90    }
91
92    pub(super) const fn reassignment() -> Self {
93        Self {
94            allow_optional_displacement: true,
95            reason: "scalar_assignment_reassignment",
96        }
97    }
98}
99
100pub(crate) fn remaining_required_count<S>(group: &ScalarAssignmentBinding<S>, solution: &S) -> u64 {
101    group.remaining_required_count(solution)
102}
103
104#[cfg(test)]
105pub(crate) fn selector_assignment_moves<S>(
106    group: &ScalarAssignmentBinding<S>,
107    solution: &S,
108    options: ScalarAssignmentMoveOptions,
109) -> Vec<CompoundScalarMove<S>>
110where
111    S: PlanningSolution,
112{
113    collect_assignment_moves(group, solution, options)
114}
115
116pub(super) fn ordered_entities<S, F>(
117    group: &ScalarAssignmentBinding<S>,
118    solution: &S,
119    mut predicate: F,
120) -> Vec<usize>
121where
122    F: FnMut(usize) -> bool,
123{
124    let mut entities = (0..group.entity_count(solution))
125        .filter(|entity_index| predicate(*entity_index))
126        .collect::<Vec<_>>();
127    entities.sort_by_key(|entity_index| {
128        (
129            group.entity_order_key(solution, *entity_index),
130            *entity_index,
131        )
132    });
133    entities
134}
135
136pub(super) fn rotate_entity_order(entities: &mut [usize], entity_offset: usize) {
137    if entities.is_empty() {
138        return;
139    }
140    let len = entities.len();
141    entities.rotate_left(entity_offset % len);
142}