solverforge_solver/phase/exhaustive/
bounder.rs1use std::fmt::Debug;
7
8use solverforge_core::domain::PlanningSolution;
9use solverforge_scoring::ScoreDirector;
10
11pub trait ScoreBounder<S: PlanningSolution>: Send + Debug {
17 fn calculate_optimistic_bound(&self, score_director: &dyn ScoreDirector<S>)
26 -> Option<S::Score>;
27
28 fn calculate_pessimistic_bound(
36 &self,
37 score_director: &dyn ScoreDirector<S>,
38 ) -> Option<S::Score> {
39 let _ = score_director;
41 None
42 }
43}
44
45#[derive(Debug, Clone, Default)]
50pub struct SimpleScoreBounder;
51
52impl SimpleScoreBounder {
53 pub fn new() -> Self {
55 Self
56 }
57}
58
59impl<S: PlanningSolution> ScoreBounder<S> for SimpleScoreBounder {
60 fn calculate_optimistic_bound(
61 &self,
62 _score_director: &dyn ScoreDirector<S>,
63 ) -> Option<S::Score> {
64 None
67 }
68}
69
70#[derive(Clone)]
75pub struct FixedOffsetBounder<S: PlanningSolution> {
76 max_improvement_per_entity: S::Score,
78}
79
80impl<S: PlanningSolution> std::fmt::Debug for FixedOffsetBounder<S> {
81 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82 f.debug_struct("FixedOffsetBounder").finish()
83 }
84}
85
86impl<S: PlanningSolution> FixedOffsetBounder<S> {
87 pub fn new(max_improvement_per_entity: S::Score) -> Self {
89 Self {
90 max_improvement_per_entity,
91 }
92 }
93}
94
95impl<S: PlanningSolution> ScoreBounder<S> for FixedOffsetBounder<S>
96where
97 S::Score: Clone + std::ops::Add<Output = S::Score> + std::ops::Mul<i32, Output = S::Score>,
98{
99 fn calculate_optimistic_bound(
100 &self,
101 score_director: &dyn ScoreDirector<S>,
102 ) -> Option<S::Score> {
103 let total = score_director.total_entity_count()?;
105
106 let current_score = score_director.working_solution().score()?;
109
110 let bound = current_score + self.max_improvement_per_entity * (total as i32);
113
114 Some(bound)
115 }
116}
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
120pub enum BounderType {
121 #[default]
123 None,
124 Simple,
126 FixedOffset,
128}
129
130impl std::fmt::Display for BounderType {
131 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132 match self {
133 BounderType::None => write!(f, "None"),
134 BounderType::Simple => write!(f, "Simple"),
135 BounderType::FixedOffset => write!(f, "FixedOffset"),
136 }
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143
144 #[test]
145 fn test_simple_bounder_returns_none() {
146 let bounder = SimpleScoreBounder::new();
147 assert!(format!("{:?}", bounder).contains("SimpleScoreBounder"));
150 }
151
152 #[test]
153 fn test_bounder_type_display() {
154 assert_eq!(format!("{}", BounderType::None), "None");
155 assert_eq!(format!("{}", BounderType::Simple), "Simple");
156 assert_eq!(format!("{}", BounderType::FixedOffset), "FixedOffset");
157 }
158
159 #[test]
160 fn test_bounder_type_default() {
161 assert_eq!(BounderType::default(), BounderType::None);
162 }
163}