1use crate::literal::{Lit, Var};
15#[allow(unused_imports)]
16use crate::prelude::*;
17
18#[derive(Debug, Clone)]
20pub struct ResolutionNode {
21 id: usize,
23 clause: Option<Vec<Lit>>,
25 parents: Vec<usize>,
27 resolved_var: Option<Var>,
29 decision_level: usize,
31 is_decision: bool,
33}
34
35impl ResolutionNode {
36 pub fn new(id: usize, clause: Vec<Lit>, decision_level: usize) -> Self {
38 Self {
39 id,
40 clause: Some(clause),
41 parents: Vec::new(),
42 resolved_var: None,
43 decision_level,
44 is_decision: false,
45 }
46 }
47
48 pub fn decision(id: usize, literal: Lit, decision_level: usize) -> Self {
50 Self {
51 id,
52 clause: Some(vec![literal]),
53 parents: Vec::new(),
54 resolved_var: None,
55 decision_level,
56 is_decision: true,
57 }
58 }
59
60 pub fn add_resolution(&mut self, parent1: usize, parent2: usize, resolved_var: Var) {
62 self.parents.push(parent1);
63 self.parents.push(parent2);
64 self.resolved_var = Some(resolved_var);
65 }
66
67 pub fn clause(&self) -> Option<&[Lit]> {
69 self.clause.as_deref()
70 }
71
72 pub fn parents(&self) -> &[usize] {
74 &self.parents
75 }
76
77 pub fn resolved_var(&self) -> Option<Var> {
79 self.resolved_var
80 }
81
82 pub fn is_decision(&self) -> bool {
84 self.is_decision
85 }
86
87 pub fn decision_level(&self) -> usize {
89 self.decision_level
90 }
91}
92
93#[derive(Debug)]
95pub struct ResolutionGraph {
96 nodes: Vec<ResolutionNode>,
98 clause_map: HashMap<u64, usize>,
100 stats: GraphStats,
102}
103
104#[derive(Debug, Default, Clone)]
106pub struct GraphStats {
107 pub resolutions: usize,
109 pub decisions: usize,
111 pub max_depth: usize,
113 pub avg_parents: f64,
115 pub frequent_vars: HashMap<Var, usize>,
117}
118
119impl ResolutionGraph {
120 pub fn new() -> Self {
122 Self {
123 nodes: Vec::new(),
124 clause_map: HashMap::new(),
125 stats: GraphStats::default(),
126 }
127 }
128
129 pub fn add_clause(&mut self, clause: Vec<Lit>, decision_level: usize) -> usize {
131 let hash = Self::hash_clause(&clause);
132
133 if let Some(&node_id) = self.clause_map.get(&hash) {
135 return node_id;
136 }
137
138 let node_id = self.nodes.len();
139 let node = ResolutionNode::new(node_id, clause, decision_level);
140
141 self.nodes.push(node);
142 self.clause_map.insert(hash, node_id);
143
144 node_id
145 }
146
147 pub fn add_decision(&mut self, literal: Lit, decision_level: usize) -> usize {
149 let node_id = self.nodes.len();
150 let node = ResolutionNode::decision(node_id, literal, decision_level);
151
152 self.nodes.push(node);
153 self.stats.decisions += 1;
154
155 node_id
156 }
157
158 pub fn add_resolution(
160 &mut self,
161 parent1_id: usize,
162 parent2_id: usize,
163 resolved_var: Var,
164 result_clause: Vec<Lit>,
165 decision_level: usize,
166 ) -> usize {
167 let result_id = self.add_clause(result_clause, decision_level);
168
169 if let Some(node) = self.nodes.get_mut(result_id)
171 && node.parents.is_empty()
172 {
173 node.add_resolution(parent1_id, parent2_id, resolved_var);
175 self.stats.resolutions += 1;
176
177 *self.stats.frequent_vars.entry(resolved_var).or_insert(0) += 1;
179 }
180
181 result_id
182 }
183
184 pub fn compute_depth(&self, node_id: usize) -> usize {
186 let mut visited = HashSet::new();
187 self.compute_depth_recursive(node_id, &mut visited)
188 }
189
190 fn compute_depth_recursive(&self, node_id: usize, visited: &mut HashSet<usize>) -> usize {
192 if visited.contains(&node_id) {
193 return 0; }
195
196 visited.insert(node_id);
197
198 let node = &self.nodes[node_id];
199 if node.parents.is_empty() {
200 return 1; }
202
203 let max_parent_depth = node
204 .parents
205 .iter()
206 .map(|&parent_id| self.compute_depth_recursive(parent_id, visited))
207 .max()
208 .unwrap_or(0);
209
210 max_parent_depth + 1
211 }
212
213 pub fn analyze(&mut self) {
215 if self.nodes.is_empty() {
216 return;
217 }
218
219 self.stats.max_depth = (0..self.nodes.len())
221 .map(|id| self.compute_depth(id))
222 .max()
223 .unwrap_or(0);
224
225 let total_parents: usize = self.nodes.iter().map(|n| n.parents.len()).sum();
227 self.stats.avg_parents = total_parents as f64 / self.nodes.len() as f64;
228 }
229
230 pub fn get_frequent_vars(&self, k: usize) -> Vec<(Var, usize)> {
232 let mut vars: Vec<_> = self
233 .stats
234 .frequent_vars
235 .iter()
236 .map(|(&var, &count)| (var, count))
237 .collect();
238
239 vars.sort_by_key(|item| std::cmp::Reverse(item.1));
240 vars.truncate(k);
241 vars
242 }
243
244 pub fn clause_quality(&self, node_id: usize) -> f64 {
251 if node_id >= self.nodes.len() {
252 return f64::MAX;
253 }
254
255 let node = &self.nodes[node_id];
256 let depth = self.compute_depth(node_id) as f64;
257 let decision_level = node.decision_level as f64;
258
259 let freq_score = if let Some(clause) = node.clause() {
261 clause
262 .iter()
263 .filter_map(|lit| {
264 self.stats
265 .frequent_vars
266 .get(&lit.var())
267 .map(|&count| count as f64)
268 })
269 .sum::<f64>()
270 } else {
271 0.0
272 };
273
274 depth + decision_level / (1.0 + freq_score)
277 }
278
279 pub fn find_redundant_resolutions(&self) -> Vec<usize> {
283 let mut redundant = Vec::new();
284
285 for (i, node) in self.nodes.iter().enumerate() {
286 if node.parents.len() < 2 {
287 continue; }
289
290 if self.has_shorter_path(i) {
294 redundant.push(i);
295 }
296 }
297
298 redundant
299 }
300
301 fn has_shorter_path(&self, node_id: usize) -> bool {
303 let node = &self.nodes[node_id];
304 let Some(target_clause) = node.clause() else {
305 return false;
306 };
307
308 let target_hash = Self::hash_clause(target_clause);
309
310 let mut queue = VecDeque::new();
312 let mut visited = HashSet::new();
313 let mut depths = HashMap::new();
314
315 for (id, n) in self.nodes.iter().enumerate() {
317 if n.parents.is_empty() && id != node_id {
318 queue.push_back(id);
319 depths.insert(id, 0);
320 }
321 }
322
323 while let Some(current_id) = queue.pop_front() {
324 if visited.contains(¤t_id) {
325 continue;
326 }
327 visited.insert(current_id);
328
329 let current_depth = depths[¤t_id];
330
331 if let Some(clause) = self.nodes[current_id].clause()
333 && Self::hash_clause(clause) == target_hash
334 && current_depth < self.compute_depth(node_id)
335 {
336 return true; }
338
339 for (child_id, child) in self.nodes.iter().enumerate() {
341 if child.parents.contains(¤t_id) && !visited.contains(&child_id) {
342 queue.push_back(child_id);
343 depths.insert(child_id, current_depth + 1);
344 }
345 }
346 }
347
348 false
349 }
350
351 fn hash_clause(clause: &[Lit]) -> u64 {
353 use core::hash::BuildHasher;
354
355 let mut sorted = clause.to_vec();
356 sorted.sort_unstable_by_key(|lit| lit.code());
357
358 let build = core::hash::BuildHasherDefault::<rustc_hash::FxHasher>::default();
359 build.hash_one(&sorted)
360 }
361
362 pub fn stats(&self) -> &GraphStats {
364 &self.stats
365 }
366
367 pub fn clear(&mut self) {
369 self.nodes.clear();
370 self.clause_map.clear();
371 self.stats = GraphStats::default();
372 }
373
374 pub fn num_nodes(&self) -> usize {
376 self.nodes.len()
377 }
378
379 pub fn get_node(&self, node_id: usize) -> Option<&ResolutionNode> {
381 self.nodes.get(node_id)
382 }
383}
384
385impl Default for ResolutionGraph {
386 fn default() -> Self {
387 Self::new()
388 }
389}
390
391#[derive(Debug)]
395pub struct ResolutionAnalyzer {
396 graph: ResolutionGraph,
398 var_scores: HashMap<Var, f64>,
400 enabled: bool,
402}
403
404impl ResolutionAnalyzer {
405 pub fn new() -> Self {
407 Self {
408 graph: ResolutionGraph::new(),
409 var_scores: HashMap::new(),
410 enabled: true,
411 }
412 }
413
414 pub fn set_enabled(&mut self, enabled: bool) {
416 self.enabled = enabled;
417 }
418
419 pub fn is_enabled(&self) -> bool {
421 self.enabled
422 }
423
424 pub fn graph(&self) -> &ResolutionGraph {
426 &self.graph
427 }
428
429 pub fn graph_mut(&mut self) -> &mut ResolutionGraph {
431 &mut self.graph
432 }
433
434 pub fn analyze(&mut self) {
436 if !self.enabled {
437 return;
438 }
439
440 self.graph.analyze();
441
442 self.var_scores.clear();
444
445 for (&var, &count) in &self.graph.stats.frequent_vars {
446 let frequency_score = count as f64;
448
449 let quality_score: f64 = self
451 .graph
452 .nodes
453 .iter()
454 .filter(|node| {
455 node.clause()
456 .map(|c| c.iter().any(|lit| lit.var() == var))
457 .unwrap_or(false)
458 })
459 .map(|node| 1.0 / (1.0 + self.graph.clause_quality(node.id)))
460 .sum();
461
462 self.var_scores.insert(var, frequency_score + quality_score);
463 }
464 }
465
466 pub fn variable_importance(&self, var: Var) -> f64 {
470 self.var_scores.get(&var).copied().unwrap_or(0.0)
471 }
472
473 pub fn get_important_vars(&self, k: usize) -> Vec<(Var, f64)> {
475 let mut vars: Vec<_> = self
476 .var_scores
477 .iter()
478 .map(|(&var, &score)| (var, score))
479 .collect();
480
481 vars.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(core::cmp::Ordering::Equal));
482 vars.truncate(k);
483 vars
484 }
485
486 pub fn clear(&mut self) {
488 self.graph.clear();
489 self.var_scores.clear();
490 }
491
492 pub fn stats(&self) -> &GraphStats {
494 self.graph.stats()
495 }
496}
497
498impl Default for ResolutionAnalyzer {
499 fn default() -> Self {
500 Self::new()
501 }
502}
503
504#[cfg(test)]
505mod tests {
506 use super::*;
507
508 #[test]
509 fn test_resolution_graph_creation() {
510 let graph = ResolutionGraph::new();
511 assert_eq!(graph.num_nodes(), 0);
512 }
513
514 #[test]
515 fn test_add_clause() {
516 let mut graph = ResolutionGraph::new();
517 let v0 = Var(0);
518 let v1 = Var(1);
519
520 let clause1 = vec![Lit::pos(v0), Lit::pos(v1)];
521 let id1 = graph.add_clause(clause1.clone(), 0);
522
523 assert_eq!(id1, 0);
524 assert_eq!(graph.num_nodes(), 1);
525
526 let id2 = graph.add_clause(clause1, 0);
528 assert_eq!(id1, id2);
529 assert_eq!(graph.num_nodes(), 1);
530 }
531
532 #[test]
533 fn test_add_decision() {
534 let mut graph = ResolutionGraph::new();
535 let v0 = Var(0);
536
537 let id = graph.add_decision(Lit::pos(v0), 1);
538 assert_eq!(id, 0);
539 assert_eq!(graph.num_nodes(), 1);
540
541 let node = graph
542 .get_node(id)
543 .expect("Decision node must exist in graph");
544 assert!(node.is_decision());
545 assert_eq!(node.decision_level(), 1);
546 }
547
548 #[test]
549 fn test_resolution() {
550 let mut graph = ResolutionGraph::new();
551 let v0 = Var(0);
552 let v1 = Var(1);
553
554 let clause1 = vec![Lit::pos(v0), Lit::pos(v1)];
556 let id1 = graph.add_clause(clause1, 0);
557
558 let clause2 = vec![Lit::neg(v0), Lit::pos(v1)];
560 let id2 = graph.add_clause(clause2, 0);
561
562 let result = vec![Lit::pos(v1)];
564 let id3 = graph.add_resolution(id1, id2, v0, result, 1);
565
566 assert_eq!(graph.num_nodes(), 3);
567
568 let node = graph
569 .get_node(id3)
570 .expect("Resolution node must exist in graph");
571 assert_eq!(node.parents().len(), 2);
572 assert_eq!(node.resolved_var(), Some(v0));
573 assert_eq!(graph.stats().resolutions, 1);
574 }
575
576 #[test]
577 fn test_compute_depth() {
578 let mut graph = ResolutionGraph::new();
579 let v0 = Var(0);
580 let v1 = Var(1);
581
582 let id1 = graph.add_clause(vec![Lit::pos(v0)], 0);
584 let id2 = graph.add_clause(vec![Lit::neg(v0), Lit::pos(v1)], 0);
585 let id3 = graph.add_resolution(id1, id2, v0, vec![Lit::pos(v1)], 1);
586
587 assert_eq!(graph.compute_depth(id1), 1); assert_eq!(graph.compute_depth(id2), 1); assert_eq!(graph.compute_depth(id3), 2); }
591
592 #[test]
593 fn test_analyze() {
594 let mut graph = ResolutionGraph::new();
595 let v0 = Var(0);
596 let v1 = Var(1);
597
598 let id1 = graph.add_clause(vec![Lit::pos(v0)], 0);
599 let id2 = graph.add_clause(vec![Lit::neg(v0), Lit::pos(v1)], 0);
600 graph.add_resolution(id1, id2, v0, vec![Lit::pos(v1)], 1);
601
602 graph.analyze();
603
604 assert_eq!(graph.stats().resolutions, 1);
605 assert!(graph.stats().max_depth > 0);
606 }
607
608 #[test]
609 fn test_frequent_vars() {
610 let mut graph = ResolutionGraph::new();
611 let v0 = Var(0);
612 let v1 = Var(1);
613 let v2 = Var(2);
614
615 let id1 = graph.add_clause(vec![Lit::pos(v0)], 0);
617 let id2 = graph.add_clause(vec![Lit::neg(v0), Lit::pos(v1)], 0);
618 graph.add_resolution(id1, id2, v0, vec![Lit::pos(v1)], 1);
619
620 let id3 = graph.add_clause(vec![Lit::pos(v0), Lit::pos(v2)], 0);
621 let id4 = graph.add_clause(vec![Lit::neg(v0)], 0);
622 graph.add_resolution(id3, id4, v0, vec![Lit::pos(v2)], 1);
623
624 let freq = graph.get_frequent_vars(10);
625 assert!(!freq.is_empty());
626 assert_eq!(freq[0].0, v0); assert_eq!(freq[0].1, 2); }
629
630 #[test]
631 fn test_resolution_analyzer() {
632 let mut analyzer = ResolutionAnalyzer::new();
633 assert!(analyzer.is_enabled());
634
635 let v0 = Var(0);
636 let v1 = Var(1);
637
638 let id1 = analyzer.graph_mut().add_clause(vec![Lit::pos(v0)], 0);
639 let id2 = analyzer
640 .graph_mut()
641 .add_clause(vec![Lit::neg(v0), Lit::pos(v1)], 0);
642 analyzer
643 .graph_mut()
644 .add_resolution(id1, id2, v0, vec![Lit::pos(v1)], 1);
645
646 analyzer.analyze();
647
648 assert!(analyzer.variable_importance(v0) > 0.0);
650 }
651
652 #[test]
653 fn test_important_vars() {
654 let mut analyzer = ResolutionAnalyzer::new();
655 let v0 = Var(0);
656 let v1 = Var(1);
657 let v2 = Var(2);
658
659 let id1 = analyzer.graph_mut().add_clause(vec![Lit::pos(v0)], 0);
661 let id2 = analyzer
662 .graph_mut()
663 .add_clause(vec![Lit::neg(v0), Lit::pos(v1)], 0);
664 analyzer
665 .graph_mut()
666 .add_resolution(id1, id2, v0, vec![Lit::pos(v1)], 1);
667
668 let id3 = analyzer
669 .graph_mut()
670 .add_clause(vec![Lit::pos(v0), Lit::pos(v2)], 0);
671 let id4 = analyzer.graph_mut().add_clause(vec![Lit::neg(v0)], 0);
672 analyzer
673 .graph_mut()
674 .add_resolution(id3, id4, v0, vec![Lit::pos(v2)], 1);
675
676 analyzer.analyze();
677
678 let important = analyzer.get_important_vars(2);
679 assert!(!important.is_empty());
680 assert_eq!(important[0].0, v0); }
682
683 #[test]
684 fn test_clear() {
685 let mut analyzer = ResolutionAnalyzer::new();
686 let v0 = Var(0);
687
688 analyzer.graph_mut().add_clause(vec![Lit::pos(v0)], 0);
689 assert_eq!(analyzer.graph().num_nodes(), 1);
690
691 analyzer.clear();
692 assert_eq!(analyzer.graph().num_nodes(), 0);
693 }
694
695 #[test]
696 fn test_clause_quality() {
697 let mut graph = ResolutionGraph::new();
698 let v0 = Var(0);
699 let v1 = Var(1);
700
701 let id1 = graph.add_clause(vec![Lit::pos(v0)], 0);
702 let id2 = graph.add_clause(vec![Lit::neg(v0), Lit::pos(v1)], 0);
703 let id3 = graph.add_resolution(id1, id2, v0, vec![Lit::pos(v1)], 1);
704
705 graph.analyze();
706
707 let quality1 = graph.clause_quality(id1);
709 let quality3 = graph.clause_quality(id3);
710
711 assert!(quality1 <= quality3);
712 }
713
714 #[test]
715 fn test_disabled_analyzer() {
716 let mut analyzer = ResolutionAnalyzer::new();
717 analyzer.set_enabled(false);
718 assert!(!analyzer.is_enabled());
719
720 let v0 = Var(0);
721 analyzer.graph_mut().add_clause(vec![Lit::pos(v0)], 0);
722
723 analyzer.analyze();
725 assert!(analyzer.var_scores.is_empty());
726 }
727}