Skip to main content

oxiz_sat/
stabilization.rs

1//! Stabilization modes for search strategy
2//!
3//! This module implements focused and diversification modes to balance
4//! exploitation and exploration in the search strategy.
5
6#[allow(unused_imports)]
7use crate::prelude::*;
8
9/// Search mode for stabilization
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum SearchMode {
12    /// Focused mode: exploit current search direction
13    /// - Use saved phases aggressively
14    /// - Lower restart intervals
15    /// - Keep learned clauses longer
16    Focused,
17
18    /// Diversification mode: explore new search space
19    /// - Randomize decisions more
20    /// - Higher restart intervals
21    /// - More aggressive clause deletion
22    Diversification,
23}
24
25/// Stabilization strategy configuration
26#[derive(Debug, Clone)]
27pub struct StabilizationConfig {
28    /// Number of conflicts in focused mode before switching
29    pub focused_conflicts: u64,
30
31    /// Number of conflicts in diversification mode before switching
32    pub diversification_conflicts: u64,
33
34    /// Minimum focused period (conflicts)
35    pub min_focused: u64,
36
37    /// Maximum focused period (conflicts)
38    pub max_focused: u64,
39
40    /// Focused mode phase saving weight (0.0 = random, 1.0 = always use saved)
41    pub focused_phase_weight: f64,
42
43    /// Diversification mode phase saving weight
44    pub diversification_phase_weight: f64,
45
46    /// Focused mode randomization probability
47    pub focused_random_prob: f64,
48
49    /// Diversification mode randomization probability
50    pub diversification_random_prob: f64,
51
52    /// Enable dynamic adjustment
53    pub dynamic_adjustment: bool,
54}
55
56impl StabilizationConfig {
57    /// Create default configuration
58    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    /// Create aggressive focused configuration
73    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    /// Create balanced configuration
88    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
99/// Stabilization manager
100pub struct StabilizationManager {
101    /// Current search mode
102    mode: SearchMode,
103
104    /// Configuration
105    config: StabilizationConfig,
106
107    /// Conflicts in current mode
108    conflicts_in_mode: u64,
109
110    /// Total conflicts in focused mode
111    total_focused_conflicts: u64,
112
113    /// Total conflicts in diversification mode
114    total_diversification_conflicts: u64,
115
116    /// Number of mode switches
117    num_switches: u64,
118
119    /// Progress score in current mode
120    progress_score: f64,
121
122    /// Best progress score in current focused period
123    best_focused_progress: f64,
124}
125
126impl StabilizationManager {
127    /// Create a new stabilization manager
128    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    /// Get current search mode
142    pub fn mode(&self) -> SearchMode {
143        self.mode
144    }
145
146    /// Record a conflict and check if mode should switch
147    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    /// Switch to focused mode
171    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    /// Switch to diversification mode
179    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    /// Record progress (e.g., based on learned clauses or LBD)
186    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            // Dynamically extend focused period if making good progress
193            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    /// Get phase saving weight for current mode
203    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    /// Get randomization probability for current mode
211    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    /// Get restart interval multiplier for current mode
219    pub fn restart_multiplier(&self) -> f64 {
220        match self.mode {
221            SearchMode::Focused => 0.8,         // Shorter restarts in focused mode
222            SearchMode::Diversification => 1.5, // Longer restarts in diversification
223        }
224    }
225
226    /// Get clause deletion aggressiveness for current mode
227    pub fn deletion_aggressiveness(&self) -> f64 {
228        match self.mode {
229            SearchMode::Focused => 0.5,         // Keep more clauses in focused mode
230            SearchMode::Diversification => 1.5, // Delete more aggressively
231        }
232    }
233
234    /// Force switch to a specific mode
235    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    /// Get statistics
244    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    /// Reset the manager
256    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/// Statistics for stabilization
274#[derive(Debug, Clone)]
275pub struct StabilizationStats {
276    /// Current mode
277    pub current_mode: SearchMode,
278    /// Conflicts in current mode
279    pub conflicts_in_mode: u64,
280    /// Total focused conflicts
281    pub total_focused_conflicts: u64,
282    /// Total diversification conflicts
283    pub total_diversification_conflicts: u64,
284    /// Number of mode switches
285    pub num_switches: u64,
286    /// Current progress score
287    pub progress_score: f64,
288}
289
290impl StabilizationStats {
291    /// Get focused ratio
292    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    /// Get average conflicts per mode
301    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        // Start in focused mode
333        assert_eq!(manager.mode(), SearchMode::Focused);
334
335        // After 10 conflicts, should switch to diversification
336        for _ in 0..10 {
337            manager.on_conflict();
338        }
339        assert_eq!(manager.mode(), SearchMode::Diversification);
340
341        // After 5 more conflicts, should switch back to focused
342        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}