solverforge_solver/phase/exhaustive/
phase.rs1use std::collections::BinaryHeap;
4use std::fmt::Debug;
5
6use solverforge_core::domain::PlanningSolution;
7use solverforge_scoring::Director;
8
9use crate::phase::Phase;
10use crate::scope::ProgressCallback;
11use crate::scope::{PhaseScope, SolverScope};
12
13use super::config::ExhaustiveSearchConfig;
14use super::decider::ExhaustiveSearchDecider;
15use super::exploration_type::ExplorationType;
16use super::node::ExhaustiveSearchNode;
17use super::priority_node::PriorityNode;
18
19pub struct ExhaustiveSearchPhase<Dec> {
56 decider: Dec,
58 config: ExhaustiveSearchConfig,
60}
61
62impl<Dec: Debug> Debug for ExhaustiveSearchPhase<Dec> {
63 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 f.debug_struct("ExhaustiveSearchPhase")
65 .field("decider", &self.decider)
66 .field("config", &self.config)
67 .finish()
68 }
69}
70
71impl<Dec> ExhaustiveSearchPhase<Dec> {
72 pub fn new(decider: Dec, config: ExhaustiveSearchConfig) -> Self {
73 Self { decider, config }
74 }
75
76 pub fn depth_first(decider: Dec) -> Self {
77 Self::new(
78 decider,
79 ExhaustiveSearchConfig {
80 exploration_type: ExplorationType::DepthFirst,
81 ..Default::default()
82 },
83 )
84 }
85
86 pub fn breadth_first(decider: Dec) -> Self {
87 Self::new(
88 decider,
89 ExhaustiveSearchConfig {
90 exploration_type: ExplorationType::BreadthFirst,
91 ..Default::default()
92 },
93 )
94 }
95
96 pub fn score_first(decider: Dec) -> Self {
97 Self::new(
98 decider,
99 ExhaustiveSearchConfig {
100 exploration_type: ExplorationType::ScoreFirst,
101 ..Default::default()
102 },
103 )
104 }
105
106 pub fn phase_type_name(&self) -> &'static str {
107 "ExhaustiveSearch"
108 }
109}
110
111impl<S, D, BestCb, Dec> Phase<S, D, BestCb> for ExhaustiveSearchPhase<Dec>
112where
113 S: PlanningSolution,
114 D: Director<S>,
115 BestCb: ProgressCallback<S>,
116 Dec: ExhaustiveSearchDecider<S, D>,
117{
118 fn solve(&mut self, solver_scope: &mut SolverScope<S, D, BestCb>) {
119 let mut phase_scope = PhaseScope::new(solver_scope, 0);
120
121 let total_entities = self.decider.total_entities(phase_scope.score_director());
123 if total_entities == 0 {
124 return;
125 }
126
127 let initial_score = phase_scope.calculate_score();
129
130 let root = ExhaustiveSearchNode::root(initial_score);
132
133 let mut best_score: Option<S::Score> = None;
135 let mut nodes_explored: u64 = 0;
136
137 let mut frontier: BinaryHeap<PriorityNode<S>> = BinaryHeap::new();
139 frontier.push(PriorityNode::new(0, root, self.config.exploration_type));
140
141 let mut all_nodes: Vec<ExhaustiveSearchNode<S>> = Vec::new();
143
144 while let Some(priority_node) = frontier.pop() {
145 let node = priority_node.node;
146 let node_index = all_nodes.len();
147
148 if let Some(limit) = self.config.node_limit {
150 if nodes_explored >= limit {
151 break;
152 }
153 }
154
155 if let Some(limit) = self.config.depth_limit {
157 if node.depth() >= limit {
158 continue;
159 }
160 }
161
162 if self.config.enable_pruning {
164 if let Some(ref best) = best_score {
165 if node.can_prune(best) {
166 continue;
167 }
168 }
169 }
170
171 nodes_explored += 1;
172
173 if node.is_leaf(total_entities) {
175 let is_better = match &best_score {
176 None => true,
177 Some(best) => node.score() > best,
178 };
179
180 if is_better {
181 best_score = Some(*node.score());
182 phase_scope.update_best_solution();
183 }
184
185 all_nodes.push(node);
186 continue;
187 }
188
189 let children = self
191 .decider
192 .expand(node_index, &node, phase_scope.score_director_mut());
193
194 all_nodes.push(node);
196
197 for child in children {
199 if self.config.enable_pruning {
201 if let Some(ref best) = best_score {
202 if child.can_prune(best) {
203 continue;
204 }
205 }
206 }
207
208 let child_index = all_nodes.len() + frontier.len();
209 frontier.push(PriorityNode::new(
210 child_index,
211 child,
212 self.config.exploration_type,
213 ));
214 }
215 }
216 }
217
218 fn phase_type_name(&self) -> &'static str {
219 "ExhaustiveSearch"
220 }
221}
222
223#[cfg(test)]
224#[path = "phase_tests.rs"]
225mod tests;