oxiz_sat/
clause_size_manager.rs1#[allow(unused_imports)]
12use crate::prelude::*;
13
14#[derive(Debug, Clone, Default)]
16pub struct SizeManagerStats {
17 pub size_rejected: usize,
19 pub total_considered: usize,
21 pub avg_accepted_size: f64,
23 pub avg_rejected_size: f64,
25 pub current_limit: usize,
27}
28
29impl SizeManagerStats {
30 pub fn display(&self) {
32 println!("Clause Size Manager Statistics:");
33 println!(" Total considered: {}", self.total_considered);
34 println!(" Size rejected: {}", self.size_rejected);
35 if self.total_considered > 0 {
36 let accept_rate = 100.0 * (self.total_considered - self.size_rejected) as f64
37 / self.total_considered as f64;
38 println!(" Acceptance rate: {:.1}%", accept_rate);
39 }
40 println!(" Avg accepted size: {:.1}", self.avg_accepted_size);
41 println!(" Avg rejected size: {:.1}", self.avg_rejected_size);
42 println!(" Current limit: {}", self.current_limit);
43 }
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum SizeAdjustmentStrategy {
49 Fixed,
51 Geometric,
53 Adaptive,
55 Luby,
57}
58
59#[derive(Debug)]
64pub struct ClauseSizeManager {
65 current_limit: usize,
67 min_limit: usize,
69 max_limit: usize,
71 strategy: SizeAdjustmentStrategy,
73 growth_factor: f64,
75 stats: SizeManagerStats,
77 conflicts_since_adjustment: u64,
79 adjustment_interval: u64,
81 accepted_size_sum: u64,
83 accepted_count: usize,
85 rejected_size_sum: u64,
87}
88
89impl Default for ClauseSizeManager {
90 fn default() -> Self {
91 Self::new()
92 }
93}
94
95impl ClauseSizeManager {
96 #[must_use]
104 pub fn new() -> Self {
105 Self {
106 current_limit: 30,
107 min_limit: 10,
108 max_limit: 100,
109 strategy: SizeAdjustmentStrategy::Geometric,
110 growth_factor: 1.1,
111 stats: SizeManagerStats::default(),
112 conflicts_since_adjustment: 0,
113 adjustment_interval: 10000,
114 accepted_size_sum: 0,
115 accepted_count: 0,
116 rejected_size_sum: 0,
117 }
118 }
119
120 #[must_use]
122 pub fn with_limits(min: usize, initial: usize, max: usize) -> Self {
123 Self {
124 current_limit: initial,
125 min_limit: min,
126 max_limit: max,
127 strategy: SizeAdjustmentStrategy::Geometric,
128 growth_factor: 1.1,
129 stats: SizeManagerStats::default(),
130 conflicts_since_adjustment: 0,
131 adjustment_interval: 10000,
132 accepted_size_sum: 0,
133 accepted_count: 0,
134 rejected_size_sum: 0,
135 }
136 }
137
138 pub fn set_strategy(&mut self, strategy: SizeAdjustmentStrategy) {
140 self.strategy = strategy;
141 }
142
143 pub fn should_learn(&mut self, size: usize, lbd: u32) -> bool {
147 self.stats.total_considered += 1;
148
149 if lbd <= 2 {
151 self.accepted_size_sum += size as u64;
152 self.accepted_count += 1;
153 return true;
154 }
155
156 if size <= self.current_limit {
158 self.accepted_size_sum += size as u64;
159 self.accepted_count += 1;
160 true
161 } else {
162 self.stats.size_rejected += 1;
163 self.rejected_size_sum += size as u64;
164 false
165 }
166 }
167
168 pub fn adjust_limit(&mut self, conflicts: u64) {
170 self.conflicts_since_adjustment += 1;
171
172 if self.conflicts_since_adjustment < self.adjustment_interval {
173 return;
174 }
175
176 self.conflicts_since_adjustment = 0;
177
178 if self.accepted_count > 0 {
180 self.stats.avg_accepted_size =
181 self.accepted_size_sum as f64 / self.accepted_count as f64;
182 }
183 if self.stats.size_rejected > 0 {
184 self.stats.avg_rejected_size =
185 self.rejected_size_sum as f64 / self.stats.size_rejected as f64;
186 }
187 self.stats.current_limit = self.current_limit;
188
189 match self.strategy {
190 SizeAdjustmentStrategy::Fixed => {
191 }
193 SizeAdjustmentStrategy::Geometric => {
194 let new_limit = (self.current_limit as f64 * self.growth_factor) as usize;
196 self.current_limit = new_limit.min(self.max_limit);
197 }
198 SizeAdjustmentStrategy::Adaptive => {
199 let rejection_rate = if self.stats.total_considered > 0 {
201 self.stats.size_rejected as f64 / self.stats.total_considered as f64
202 } else {
203 0.0
204 };
205
206 if rejection_rate > 0.3 {
207 self.current_limit = (self.current_limit + 5).min(self.max_limit);
209 } else if rejection_rate < 0.1 {
210 self.current_limit = (self.current_limit.saturating_sub(2)).max(self.min_limit);
212 }
213 }
214 SizeAdjustmentStrategy::Luby => {
215 let luby_value = self.luby((conflicts / self.adjustment_interval) as u32);
217 self.current_limit = (self.min_limit + luby_value as usize).min(self.max_limit);
218 }
219 }
220 }
221
222 #[allow(clippy::only_used_in_recursion)]
225 fn luby(&self, i: u32) -> u32 {
226 let mut power = 1u32;
227
228 while power * 2 <= i + 1 {
230 power *= 2;
231 }
232
233 if power == i + 1 {
234 if power == 1 { 1 } else { power / 2 }
236 } else {
237 self.luby(i + 1 - power)
239 }
240 }
241
242 #[must_use]
244 pub fn current_limit(&self) -> usize {
245 self.current_limit
246 }
247
248 #[must_use]
250 pub fn stats(&self) -> &SizeManagerStats {
251 &self.stats
252 }
253
254 pub fn reset_stats(&mut self) {
256 self.stats = SizeManagerStats::default();
257 self.stats.current_limit = self.current_limit;
258 self.accepted_size_sum = 0;
259 self.accepted_count = 0;
260 self.rejected_size_sum = 0;
261 }
262}
263
264#[cfg(test)]
265mod tests {
266 use super::*;
267
268 #[test]
269 fn test_size_manager_creation() {
270 let manager = ClauseSizeManager::new();
271 assert_eq!(manager.current_limit(), 30);
272 }
273
274 #[test]
275 fn test_should_learn_within_limit() {
276 let mut manager = ClauseSizeManager::new();
277 assert!(manager.should_learn(20, 5)); assert!(manager.should_learn(30, 5)); }
280
281 #[test]
282 fn test_should_learn_exceeds_limit() {
283 let mut manager = ClauseSizeManager::new();
284 assert!(!manager.should_learn(50, 5)); assert_eq!(manager.stats().size_rejected, 1);
286 }
287
288 #[test]
289 fn test_should_learn_high_quality() {
290 let mut manager = ClauseSizeManager::new();
291 assert!(manager.should_learn(100, 2));
293 assert_eq!(manager.stats().size_rejected, 0);
294 }
295
296 #[test]
297 fn test_geometric_growth() {
298 let mut manager = ClauseSizeManager::new();
299 manager.set_strategy(SizeAdjustmentStrategy::Geometric);
300
301 let initial = manager.current_limit();
302 manager.conflicts_since_adjustment = manager.adjustment_interval;
304 manager.adjust_limit(10000);
305
306 assert!(manager.current_limit() > initial);
307 }
308
309 #[test]
310 fn test_fixed_strategy() {
311 let mut manager = ClauseSizeManager::new();
312 manager.set_strategy(SizeAdjustmentStrategy::Fixed);
313
314 let initial = manager.current_limit();
315 manager.conflicts_since_adjustment = manager.adjustment_interval;
316 manager.adjust_limit(10000);
317
318 assert_eq!(manager.current_limit(), initial);
319 }
320
321 #[test]
322 fn test_adaptive_increase() {
323 let mut manager = ClauseSizeManager::new();
324 manager.set_strategy(SizeAdjustmentStrategy::Adaptive);
325
326 for _ in 0..100 {
328 manager.should_learn(50, 5); }
330
331 let initial = manager.current_limit();
332 manager.conflicts_since_adjustment = manager.adjustment_interval;
333 manager.adjust_limit(10000);
334
335 assert!(manager.current_limit() > initial);
336 }
337
338 #[test]
339 fn test_adaptive_decrease() {
340 let mut manager = ClauseSizeManager::new();
341 manager.set_strategy(SizeAdjustmentStrategy::Adaptive);
342
343 for _ in 0..100 {
345 manager.should_learn(10, 5); }
347
348 let initial = manager.current_limit();
349 manager.conflicts_since_adjustment = manager.adjustment_interval;
350 manager.adjust_limit(10000);
351
352 assert!(manager.current_limit() < initial);
353 }
354
355 #[test]
356 fn test_luby_strategy_available() {
357 let mut manager = ClauseSizeManager::new();
358 manager.set_strategy(SizeAdjustmentStrategy::Luby);
359 manager.conflicts_since_adjustment = manager.adjustment_interval;
360 manager.adjust_limit(10000);
361 assert!(manager.current_limit() > 0);
363 }
364
365 #[test]
366 fn test_stats_tracking() {
367 let mut manager = ClauseSizeManager::new();
368
369 manager.should_learn(20, 5);
370 manager.should_learn(50, 5);
371 manager.should_learn(15, 5);
372
373 let stats = manager.stats();
374 assert_eq!(stats.total_considered, 3);
375 assert_eq!(stats.size_rejected, 1); }
377}