1use crate::clause::{ClauseDatabase, ClauseId};
15use crate::literal::Lit;
16#[allow(unused_imports)]
17use crate::prelude::*;
18
19#[derive(Debug, Default, Clone)]
21pub struct SubsumptionStats {
22 pub checks_performed: u64,
24 pub clauses_subsumed: u64,
26 pub forward_subsumptions: u64,
28 pub backward_subsumptions: u64,
30}
31
32pub struct SubsumptionChecker {
34 clause_lits: FxHashSet<Lit>,
36 candidate_lits: FxHashSet<Lit>,
38 max_check_size: usize,
40 stats: SubsumptionStats,
42}
43
44impl SubsumptionChecker {
45 pub fn new(max_check_size: usize) -> Self {
47 Self {
48 clause_lits: FxHashSet::default(),
49 candidate_lits: FxHashSet::default(),
50 max_check_size,
51 stats: SubsumptionStats::default(),
52 }
53 }
54
55 #[must_use]
57 pub fn stats(&self) -> &SubsumptionStats {
58 &self.stats
59 }
60
61 fn subsumes(&mut self, clause_a: &[Lit], clause_b: &[Lit]) -> bool {
65 if clause_a.len() > clause_b.len() {
67 return false;
68 }
69
70 self.candidate_lits.clear();
72 for &lit in clause_b {
73 self.candidate_lits.insert(lit);
74 }
75
76 for &lit in clause_a {
78 if !self.candidate_lits.contains(&lit) {
79 return false;
80 }
81 }
82
83 true
84 }
85
86 pub fn check_forward_subsumption(
90 &mut self,
91 new_clause: &[Lit],
92 clauses: &ClauseDatabase,
93 ) -> Vec<ClauseId> {
94 if new_clause.len() > self.max_check_size {
96 return Vec::new();
97 }
98
99 let mut subsumed = Vec::new();
100
101 for clause_id in clauses.iter_ids() {
103 let Some(clause) = clauses.get(clause_id) else {
104 continue;
105 };
106
107 if !clause.learned || clause.deleted {
109 continue;
110 }
111
112 if clause.len() > self.max_check_size {
114 continue;
115 }
116
117 self.stats.checks_performed += 1;
118
119 if self.subsumes(new_clause, &clause.lits) {
121 subsumed.push(clause_id);
122 self.stats.forward_subsumptions += 1;
123 }
124 }
125
126 self.stats.clauses_subsumed += subsumed.len() as u64;
127 subsumed
128 }
129
130 pub fn check_backward_subsumption(
134 &mut self,
135 new_clause: &[Lit],
136 clauses: &ClauseDatabase,
137 ) -> bool {
138 if new_clause.len() > self.max_check_size {
140 return false;
141 }
142
143 self.clause_lits.clear();
145 for &lit in new_clause {
146 self.clause_lits.insert(lit);
147 }
148
149 for clause_id in clauses.iter_ids() {
151 let Some(clause) = clauses.get(clause_id) else {
152 continue;
153 };
154
155 if !clause.learned || clause.deleted {
157 continue;
158 }
159
160 if clause.len() > new_clause.len() {
162 continue;
163 }
164
165 if clause.len() > self.max_check_size {
167 continue;
168 }
169
170 self.stats.checks_performed += 1;
171
172 if self.subsumes(&clause.lits, new_clause) {
174 self.stats.backward_subsumptions += 1;
175 return true;
176 }
177 }
178
179 false
180 }
181
182 pub fn check_subsumption(
188 &mut self,
189 new_clause: &[Lit],
190 clauses: &ClauseDatabase,
191 ) -> (bool, Vec<ClauseId>) {
192 let is_subsumed = self.check_backward_subsumption(new_clause, clauses);
194
195 if is_subsumed {
197 return (true, Vec::new());
198 }
199
200 let subsumed = self.check_forward_subsumption(new_clause, clauses);
202
203 (false, subsumed)
204 }
205
206 pub fn reset_stats(&mut self) {
208 self.stats = SubsumptionStats::default();
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215 use crate::literal::Var;
216
217 #[test]
218 fn test_subsumption_creation() {
219 let checker = SubsumptionChecker::new(10);
220 assert_eq!(checker.max_check_size, 10);
221 }
222
223 #[test]
224 fn test_subsumes() {
225 let mut checker = SubsumptionChecker::new(20);
226
227 let clause_a = vec![Lit::pos(Var::new(0)), Lit::pos(Var::new(1))];
229 let clause_b = vec![
230 Lit::pos(Var::new(0)),
231 Lit::pos(Var::new(1)),
232 Lit::pos(Var::new(2)),
233 ];
234
235 assert!(checker.subsumes(&clause_a, &clause_b));
236 assert!(!checker.subsumes(&clause_b, &clause_a)); }
238
239 #[test]
240 fn test_no_subsumption() {
241 let mut checker = SubsumptionChecker::new(20);
242
243 let clause_a = vec![Lit::pos(Var::new(0)), Lit::pos(Var::new(1))];
245 let clause_b = vec![Lit::pos(Var::new(0)), Lit::pos(Var::new(2))];
246
247 assert!(!checker.subsumes(&clause_a, &clause_b));
248 }
249
250 #[test]
251 fn test_forward_subsumption() {
252 let mut checker = SubsumptionChecker::new(20);
253 let mut db = ClauseDatabase::new();
254
255 let _old_id = db.add_learned([
257 Lit::pos(Var::new(0)),
258 Lit::pos(Var::new(1)),
259 Lit::pos(Var::new(2)),
260 ]);
261
262 let new_clause = vec![Lit::pos(Var::new(0)), Lit::pos(Var::new(1))];
264
265 let subsumed = checker.check_forward_subsumption(&new_clause, &db);
266 assert_eq!(subsumed.len(), 1);
267 }
268
269 #[test]
270 fn test_backward_subsumption() {
271 let mut checker = SubsumptionChecker::new(20);
272 let mut db = ClauseDatabase::new();
273
274 let _old_id = db.add_learned([Lit::pos(Var::new(0)), Lit::pos(Var::new(1))]);
276
277 let new_clause = vec![
279 Lit::pos(Var::new(0)),
280 Lit::pos(Var::new(1)),
281 Lit::pos(Var::new(2)),
282 ];
283
284 assert!(checker.check_backward_subsumption(&new_clause, &db));
285 }
286
287 #[test]
288 fn test_stats() {
289 let mut checker = SubsumptionChecker::new(20);
290 let stats = checker.stats();
291 assert_eq!(stats.checks_performed, 0);
292 assert_eq!(stats.clauses_subsumed, 0);
293
294 checker.reset_stats();
295 let stats = checker.stats();
296 assert_eq!(stats.checks_performed, 0);
297 }
298}