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, D: ScoreDirector<S>>: Send + Debug {
17 fn calculate_optimistic_bound(&self, score_director: &D) -> Option<S::Score>;
26
27 fn calculate_pessimistic_bound(&self, score_director: &D) -> Option<S::Score> {
35 let _ = score_director;
37 None
38 }
39}
40
41#[derive(Debug, Clone, Default)]
46pub struct SimpleScoreBounder;
47
48impl SimpleScoreBounder {
49 pub fn new() -> Self {
51 Self
52 }
53}
54
55impl<S: PlanningSolution, D: ScoreDirector<S>> ScoreBounder<S, D> for SimpleScoreBounder {
56 fn calculate_optimistic_bound(&self, _score_director: &D) -> Option<S::Score> {
57 None
60 }
61}
62
63#[derive(Clone)]
68pub struct FixedOffsetBounder<S: PlanningSolution> {
69 max_improvement_per_entity: S::Score,
71}
72
73impl<S: PlanningSolution> std::fmt::Debug for FixedOffsetBounder<S> {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 f.debug_struct("FixedOffsetBounder").finish()
76 }
77}
78
79impl<S: PlanningSolution> FixedOffsetBounder<S> {
80 pub fn new(max_improvement_per_entity: S::Score) -> Self {
82 Self {
83 max_improvement_per_entity,
84 }
85 }
86}
87
88impl<S: PlanningSolution, D: ScoreDirector<S>> ScoreBounder<S, D> for FixedOffsetBounder<S>
89where
90 S::Score: Clone + std::ops::Add<Output = S::Score> + std::ops::Mul<i32, Output = S::Score>,
91{
92 fn calculate_optimistic_bound(&self, score_director: &D) -> Option<S::Score> {
93 let total = score_director.total_entity_count()?;
95
96 let current_score = score_director.working_solution().score()?;
99
100 let bound = current_score + self.max_improvement_per_entity * (total as i32);
103
104 Some(bound)
105 }
106}
107
108#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
110pub enum BounderType {
111 #[default]
113 None,
114 Simple,
116 FixedOffset,
118}
119
120impl std::fmt::Display for BounderType {
121 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122 match self {
123 BounderType::None => write!(f, "None"),
124 BounderType::Simple => write!(f, "Simple"),
125 BounderType::FixedOffset => write!(f, "FixedOffset"),
126 }
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn test_simple_bounder_returns_none() {
136 let bounder = SimpleScoreBounder::new();
137 assert!(format!("{:?}", bounder).contains("SimpleScoreBounder"));
140 }
141
142 #[test]
143 fn test_bounder_type_display() {
144 assert_eq!(format!("{}", BounderType::None), "None");
145 assert_eq!(format!("{}", BounderType::Simple), "Simple");
146 assert_eq!(format!("{}", BounderType::FixedOffset), "FixedOffset");
147 }
148
149 #[test]
150 fn test_bounder_type_default() {
151 assert_eq!(BounderType::default(), BounderType::None);
152 }
153}