oxiz_sat/
stabilization.rs1#[allow(unused_imports)]
7use crate::prelude::*;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum SearchMode {
12 Focused,
17
18 Diversification,
23}
24
25#[derive(Debug, Clone)]
27pub struct StabilizationConfig {
28 pub focused_conflicts: u64,
30
31 pub diversification_conflicts: u64,
33
34 pub min_focused: u64,
36
37 pub max_focused: u64,
39
40 pub focused_phase_weight: f64,
42
43 pub diversification_phase_weight: f64,
45
46 pub focused_random_prob: f64,
48
49 pub diversification_random_prob: f64,
51
52 pub dynamic_adjustment: bool,
54}
55
56impl StabilizationConfig {
57 pub fn default_config() -> Self {
59 Self {
60 focused_conflicts: 5000,
61 diversification_conflicts: 1000,
62 min_focused: 1000,
63 max_focused: 100000,
64 focused_phase_weight: 0.95,
65 diversification_phase_weight: 0.5,
66 focused_random_prob: 0.01,
67 diversification_random_prob: 0.1,
68 dynamic_adjustment: true,
69 }
70 }
71
72 pub fn aggressive_focused() -> Self {
74 Self {
75 focused_conflicts: 10000,
76 diversification_conflicts: 500,
77 min_focused: 5000,
78 max_focused: 200000,
79 focused_phase_weight: 0.98,
80 diversification_phase_weight: 0.3,
81 focused_random_prob: 0.005,
82 diversification_random_prob: 0.15,
83 dynamic_adjustment: true,
84 }
85 }
86
87 pub fn balanced() -> Self {
89 Self::default_config()
90 }
91}
92
93impl Default for StabilizationConfig {
94 fn default() -> Self {
95 Self::default_config()
96 }
97}
98
99pub struct StabilizationManager {
101 mode: SearchMode,
103
104 config: StabilizationConfig,
106
107 conflicts_in_mode: u64,
109
110 total_focused_conflicts: u64,
112
113 total_diversification_conflicts: u64,
115
116 num_switches: u64,
118
119 progress_score: f64,
121
122 best_focused_progress: f64,
124}
125
126impl StabilizationManager {
127 pub fn new(config: StabilizationConfig) -> Self {
129 Self {
130 mode: SearchMode::Focused,
131 config,
132 conflicts_in_mode: 0,
133 total_focused_conflicts: 0,
134 total_diversification_conflicts: 0,
135 num_switches: 0,
136 progress_score: 0.0,
137 best_focused_progress: 0.0,
138 }
139 }
140
141 pub fn mode(&self) -> SearchMode {
143 self.mode
144 }
145
146 pub fn on_conflict(&mut self) -> bool {
148 self.conflicts_in_mode += 1;
149
150 match self.mode {
151 SearchMode::Focused => {
152 self.total_focused_conflicts += 1;
153 if self.conflicts_in_mode >= self.config.focused_conflicts {
154 self.switch_to_diversification();
155 return true;
156 }
157 }
158 SearchMode::Diversification => {
159 self.total_diversification_conflicts += 1;
160 if self.conflicts_in_mode >= self.config.diversification_conflicts {
161 self.switch_to_focused();
162 return true;
163 }
164 }
165 }
166
167 false
168 }
169
170 fn switch_to_focused(&mut self) {
172 self.mode = SearchMode::Focused;
173 self.conflicts_in_mode = 0;
174 self.num_switches += 1;
175 self.best_focused_progress = 0.0;
176 }
177
178 fn switch_to_diversification(&mut self) {
180 self.mode = SearchMode::Diversification;
181 self.conflicts_in_mode = 0;
182 self.num_switches += 1;
183 }
184
185 pub fn record_progress(&mut self, score: f64) {
187 self.progress_score = score;
188
189 if self.mode == SearchMode::Focused && score > self.best_focused_progress {
190 self.best_focused_progress = score;
191
192 if self.config.dynamic_adjustment {
194 let extension = (self.config.focused_conflicts as f64 * 0.1) as u64;
195 if self.config.focused_conflicts + extension <= self.config.max_focused {
196 self.config.focused_conflicts += extension;
197 }
198 }
199 }
200 }
201
202 pub fn phase_weight(&self) -> f64 {
204 match self.mode {
205 SearchMode::Focused => self.config.focused_phase_weight,
206 SearchMode::Diversification => self.config.diversification_phase_weight,
207 }
208 }
209
210 pub fn random_prob(&self) -> f64 {
212 match self.mode {
213 SearchMode::Focused => self.config.focused_random_prob,
214 SearchMode::Diversification => self.config.diversification_random_prob,
215 }
216 }
217
218 pub fn restart_multiplier(&self) -> f64 {
220 match self.mode {
221 SearchMode::Focused => 0.8, SearchMode::Diversification => 1.5, }
224 }
225
226 pub fn deletion_aggressiveness(&self) -> f64 {
228 match self.mode {
229 SearchMode::Focused => 0.5, SearchMode::Diversification => 1.5, }
232 }
233
234 pub fn force_mode(&mut self, mode: SearchMode) {
236 if self.mode != mode {
237 self.mode = mode;
238 self.conflicts_in_mode = 0;
239 self.num_switches += 1;
240 }
241 }
242
243 pub fn stats(&self) -> StabilizationStats {
245 StabilizationStats {
246 current_mode: self.mode,
247 conflicts_in_mode: self.conflicts_in_mode,
248 total_focused_conflicts: self.total_focused_conflicts,
249 total_diversification_conflicts: self.total_diversification_conflicts,
250 num_switches: self.num_switches,
251 progress_score: self.progress_score,
252 }
253 }
254
255 pub fn reset(&mut self) {
257 self.mode = SearchMode::Focused;
258 self.conflicts_in_mode = 0;
259 self.total_focused_conflicts = 0;
260 self.total_diversification_conflicts = 0;
261 self.num_switches = 0;
262 self.progress_score = 0.0;
263 self.best_focused_progress = 0.0;
264 }
265}
266
267impl Default for StabilizationManager {
268 fn default() -> Self {
269 Self::new(StabilizationConfig::default())
270 }
271}
272
273#[derive(Debug, Clone)]
275pub struct StabilizationStats {
276 pub current_mode: SearchMode,
278 pub conflicts_in_mode: u64,
280 pub total_focused_conflicts: u64,
282 pub total_diversification_conflicts: u64,
284 pub num_switches: u64,
286 pub progress_score: f64,
288}
289
290impl StabilizationStats {
291 pub fn focused_ratio(&self) -> f64 {
293 let total = self.total_focused_conflicts + self.total_diversification_conflicts;
294 if total == 0 {
295 return 0.5;
296 }
297 self.total_focused_conflicts as f64 / total as f64
298 }
299
300 pub fn avg_conflicts_per_mode(&self) -> f64 {
302 if self.num_switches == 0 {
303 return 0.0;
304 }
305 let total = self.total_focused_conflicts + self.total_diversification_conflicts;
306 total as f64 / self.num_switches as f64
307 }
308}
309
310#[cfg(test)]
311mod tests {
312 use super::*;
313
314 #[test]
315 fn test_stabilization_basic() {
316 let config = StabilizationConfig::default_config();
317 let manager = StabilizationManager::new(config);
318
319 assert_eq!(manager.mode(), SearchMode::Focused);
320 }
321
322 #[test]
323 fn test_mode_switching() {
324 let config = StabilizationConfig {
325 focused_conflicts: 10,
326 diversification_conflicts: 5,
327 ..StabilizationConfig::default_config()
328 };
329
330 let mut manager = StabilizationManager::new(config);
331
332 assert_eq!(manager.mode(), SearchMode::Focused);
334
335 for _ in 0..10 {
337 manager.on_conflict();
338 }
339 assert_eq!(manager.mode(), SearchMode::Diversification);
340
341 for _ in 0..5 {
343 manager.on_conflict();
344 }
345 assert_eq!(manager.mode(), SearchMode::Focused);
346 }
347
348 #[test]
349 fn test_phase_weight() {
350 let config = StabilizationConfig::default_config();
351 let manager = StabilizationManager::new(config.clone());
352
353 assert_eq!(manager.phase_weight(), config.focused_phase_weight);
354
355 let mut manager = StabilizationManager::new(config.clone());
356 manager.force_mode(SearchMode::Diversification);
357 assert_eq!(manager.phase_weight(), config.diversification_phase_weight);
358 }
359
360 #[test]
361 fn test_stats() {
362 let mut config = StabilizationConfig::default_config();
363 config.focused_conflicts = 5;
364 let mut manager = StabilizationManager::new(config);
365
366 for _ in 0..10 {
367 manager.on_conflict();
368 }
369
370 let stats = manager.stats();
371 assert!(stats.num_switches > 0);
372 assert!(stats.total_focused_conflicts > 0);
373 }
374
375 #[test]
376 fn test_progress_tracking() {
377 let config = StabilizationConfig::default_config();
378 let mut manager = StabilizationManager::new(config);
379
380 manager.record_progress(0.5);
381 assert_eq!(manager.progress_score, 0.5);
382
383 manager.record_progress(0.8);
384 assert_eq!(manager.progress_score, 0.8);
385 }
386}