1use crate::literal::Lit;
13#[allow(unused_imports)]
14use crate::prelude::*;
15
16#[derive(Debug, Clone, Default)]
18pub struct BackboneStats {
19 pub backbone_size: usize,
21 pub positive_lits: usize,
23 pub negative_lits: usize,
25 pub solver_calls: usize,
27 pub iterations: usize,
29}
30
31impl BackboneStats {
32 pub fn display(&self) {
34 println!("Backbone Computation Statistics:");
35 println!(" Backbone size: {}", self.backbone_size);
36 println!(" Positive literals: {}", self.positive_lits);
37 println!(" Negative literals: {}", self.negative_lits);
38 println!(" Solver calls: {}", self.solver_calls);
39 println!(" Iterations: {}", self.iterations);
40 if self.solver_calls > 0 {
41 println!(
42 " Avg backbone per call: {:.2}",
43 self.backbone_size as f64 / self.solver_calls as f64
44 );
45 }
46 }
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51pub enum BackboneAlgorithm {
52 Iterative,
54 BinarySearch,
56 AssumptionBased,
58}
59
60#[derive(Debug)]
62pub struct BackboneDetector {
63 backbone: HashSet<Lit>,
65 algorithm: BackboneAlgorithm,
67 stats: BackboneStats,
69}
70
71impl Default for BackboneDetector {
72 fn default() -> Self {
73 Self::new()
74 }
75}
76
77impl BackboneDetector {
78 #[must_use]
80 pub fn new() -> Self {
81 Self {
82 backbone: HashSet::new(),
83 algorithm: BackboneAlgorithm::Iterative,
84 stats: BackboneStats::default(),
85 }
86 }
87
88 #[must_use]
90 pub fn with_algorithm(algorithm: BackboneAlgorithm) -> Self {
91 Self {
92 backbone: HashSet::new(),
93 algorithm,
94 stats: BackboneStats::default(),
95 }
96 }
97
98 #[must_use]
100 pub fn is_backbone(&self, lit: Lit) -> bool {
101 self.backbone.contains(&lit)
102 }
103
104 pub fn add_backbone(&mut self, lit: Lit) {
106 if self.backbone.insert(lit) {
107 self.stats.backbone_size += 1;
108 if lit.is_pos() {
109 self.stats.positive_lits += 1;
110 } else {
111 self.stats.negative_lits += 1;
112 }
113 }
114 }
115
116 pub fn remove_backbone(&mut self, lit: Lit) {
118 if self.backbone.remove(&lit) {
119 self.stats.backbone_size = self.stats.backbone_size.saturating_sub(1);
120 if lit.is_pos() {
121 self.stats.positive_lits = self.stats.positive_lits.saturating_sub(1);
122 } else {
123 self.stats.negative_lits = self.stats.negative_lits.saturating_sub(1);
124 }
125 }
126 }
127
128 pub fn detect_iterative<F>(&mut self, candidates: &[Lit], mut is_sat_with_assumption: F)
133 where
134 F: FnMut(Lit) -> bool,
135 {
136 self.backbone.clear();
137 self.stats = BackboneStats::default();
138
139 for &lit in candidates {
140 self.stats.iterations += 1;
141 self.stats.solver_calls += 1;
142
143 if !is_sat_with_assumption(!lit) {
145 self.add_backbone(lit);
147 }
148 }
149 }
150
151 pub fn detect_binary_search<F>(&mut self, candidates: &[Lit], is_sat_with_assumptions: F)
156 where
157 F: Fn(&[Lit]) -> bool + Clone,
158 {
159 self.backbone.clear();
160 self.stats = BackboneStats::default();
161
162 let mut to_process = vec![candidates.to_vec()];
163
164 while let Some(group) = to_process.pop() {
165 if group.is_empty() {
166 continue;
167 }
168
169 self.stats.iterations += 1;
170 self.stats.solver_calls += 1;
171
172 let negated: Vec<_> = group.iter().map(|&lit| !lit).collect();
174
175 if !is_sat_with_assumptions(&negated) {
176 for &lit in &group {
178 self.add_backbone(lit);
179 }
180 } else if group.len() > 1 {
181 let mid = group.len() / 2;
183 to_process.push(group[..mid].to_vec());
184 to_process.push(group[mid..].to_vec());
185 }
186 }
188 }
189
190 #[must_use]
195 pub fn compute_rotatable(&self, all_lits: &[Lit]) -> Vec<Lit> {
196 all_lits
197 .iter()
198 .filter(|&&lit| !self.is_backbone(lit) && !self.is_backbone(!lit))
199 .copied()
200 .collect()
201 }
202
203 #[must_use]
205 pub fn backbone(&self) -> Vec<Lit> {
206 self.backbone.iter().copied().collect()
207 }
208
209 #[must_use]
211 pub fn size(&self) -> usize {
212 self.backbone.len()
213 }
214
215 #[must_use]
217 pub fn is_empty(&self) -> bool {
218 self.backbone.is_empty()
219 }
220
221 #[must_use]
223 pub fn stats(&self) -> &BackboneStats {
224 &self.stats
225 }
226
227 pub fn clear(&mut self) {
229 self.backbone.clear();
230 self.stats = BackboneStats::default();
231 }
232
233 #[must_use]
235 pub fn algorithm(&self) -> BackboneAlgorithm {
236 self.algorithm
237 }
238
239 pub fn set_algorithm(&mut self, algorithm: BackboneAlgorithm) {
241 self.algorithm = algorithm;
242 }
243}
244
245#[derive(Debug)]
249pub struct BackboneFilter {
250 detector: BackboneDetector,
252 prefer_backbone: bool,
254}
255
256impl Default for BackboneFilter {
257 fn default() -> Self {
258 Self::new()
259 }
260}
261
262impl BackboneFilter {
263 #[must_use]
265 pub fn new() -> Self {
266 Self {
267 detector: BackboneDetector::new(),
268 prefer_backbone: true,
269 }
270 }
271
272 #[must_use]
276 pub fn filter(&self, candidates: &[Lit]) -> Vec<Lit> {
277 let mut backbone = Vec::new();
278 let mut non_backbone = Vec::new();
279
280 for &lit in candidates {
281 if self.detector.is_backbone(lit) {
282 backbone.push(lit);
283 } else {
284 non_backbone.push(lit);
285 }
286 }
287
288 if self.prefer_backbone {
289 backbone.extend(non_backbone);
290 backbone
291 } else {
292 non_backbone.extend(backbone);
293 non_backbone
294 }
295 }
296
297 #[must_use]
299 pub fn detector(&self) -> &BackboneDetector {
300 &self.detector
301 }
302
303 pub fn detector_mut(&mut self) -> &mut BackboneDetector {
305 &mut self.detector
306 }
307
308 pub fn set_prefer_backbone(&mut self, prefer: bool) {
310 self.prefer_backbone = prefer;
311 }
312}
313
314#[cfg(test)]
315mod tests {
316 use super::*;
317 use crate::literal::Var;
318
319 #[test]
320 fn test_backbone_detector_creation() {
321 let detector = BackboneDetector::new();
322 assert_eq!(detector.size(), 0);
323 assert!(detector.is_empty());
324 }
325
326 #[test]
327 fn test_add_backbone() {
328 let mut detector = BackboneDetector::new();
329 let lit = Lit::pos(Var::new(0));
330
331 assert!(!detector.is_backbone(lit));
332 detector.add_backbone(lit);
333 assert!(detector.is_backbone(lit));
334 assert_eq!(detector.size(), 1);
335 }
336
337 #[test]
338 fn test_remove_backbone() {
339 let mut detector = BackboneDetector::new();
340 let lit = Lit::pos(Var::new(0));
341
342 detector.add_backbone(lit);
343 assert_eq!(detector.size(), 1);
344
345 detector.remove_backbone(lit);
346 assert_eq!(detector.size(), 0);
347 assert!(!detector.is_backbone(lit));
348 }
349
350 #[test]
351 fn test_detect_iterative() {
352 let mut detector = BackboneDetector::new();
353 let a = Lit::pos(Var::new(0));
354 let b = Lit::pos(Var::new(1));
355
356 let is_sat = |lit: Lit| -> bool {
358 lit != !a };
360
361 detector.detect_iterative(&[a, b], is_sat);
362
363 assert!(detector.is_backbone(a));
364 assert!(!detector.is_backbone(b));
365 assert_eq!(detector.size(), 1);
366 }
367
368 #[test]
369 fn test_compute_rotatable() {
370 let mut detector = BackboneDetector::new();
371 let a = Lit::pos(Var::new(0));
372 let b = Lit::pos(Var::new(1));
373 let c = Lit::pos(Var::new(2));
374
375 detector.add_backbone(a);
376
377 let rotatable = detector.compute_rotatable(&[a, b, c]);
378
379 assert!(!rotatable.contains(&a));
380 assert!(rotatable.contains(&b));
381 assert!(rotatable.contains(&c));
382 }
383
384 #[test]
385 fn test_backbone_filter() {
386 let mut filter = BackboneFilter::new();
387 let a = Lit::pos(Var::new(0));
388 let b = Lit::pos(Var::new(1));
389 let c = Lit::pos(Var::new(2));
390
391 filter.detector_mut().add_backbone(a);
392
393 let filtered = filter.filter(&[c, b, a]);
394
395 assert_eq!(filtered[0], a);
397 }
398
399 #[test]
400 fn test_statistics() {
401 let mut detector = BackboneDetector::new();
402 detector.add_backbone(Lit::pos(Var::new(0)));
403 detector.add_backbone(Lit::neg(Var::new(1)));
404
405 let stats = detector.stats();
406 assert_eq!(stats.backbone_size, 2);
407 assert_eq!(stats.positive_lits, 1);
408 assert_eq!(stats.negative_lits, 1);
409 }
410
411 #[test]
412 fn test_algorithm_selection() {
413 let mut detector = BackboneDetector::with_algorithm(BackboneAlgorithm::BinarySearch);
414 assert_eq!(detector.algorithm(), BackboneAlgorithm::BinarySearch);
415
416 detector.set_algorithm(BackboneAlgorithm::Iterative);
417 assert_eq!(detector.algorithm(), BackboneAlgorithm::Iterative);
418 }
419
420 #[test]
421 fn test_clear() {
422 let mut detector = BackboneDetector::new();
423 detector.add_backbone(Lit::pos(Var::new(0)));
424 assert!(!detector.is_empty());
425
426 detector.clear();
427 assert!(detector.is_empty());
428 assert_eq!(detector.stats().backbone_size, 0);
429 }
430}