1#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
2use crate::sat::clause::Clause;
17use crate::sat::clause_storage::LiteralStorage;
18use crate::sat::literal::Literal;
19use rustc_hash::FxHashSet;
20use std::fmt::Debug;
21use std::sync::Arc;
22
23pub trait Preprocessor<L: Literal, S: LiteralStorage<L>> {
34 fn preprocess(&self, cnf: &[Clause<L, S>]) -> Vec<Clause<L, S>>;
45}
46
47#[derive(Clone, Default)]
58pub struct PreprocessorChain<L: Literal, S: LiteralStorage<L>> {
59 preprocessors: Vec<Arc<dyn Preprocessor<L, S>>>,
60}
61
62impl<L: Literal, S: LiteralStorage<L>> Debug for PreprocessorChain<L, S> {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 f.debug_struct("PreprocessorChain")
68 .field("num_preprocessors", &self.preprocessors.len())
69 .finish()
70 }
71}
72
73impl<L: Literal, S: LiteralStorage<L>> PreprocessorChain<L, S> {
74 #[must_use]
76 #[allow(dead_code)]
77 pub const fn new() -> Self {
78 Self {
79 preprocessors: Vec::new(),
80 }
81 }
82
83 #[must_use]
97 #[allow(dead_code)]
98 pub fn add_preprocessor<P: Preprocessor<L, S> + 'static>(mut self, preprocessor: P) -> Self {
99 let arc_preprocessor = Arc::new(preprocessor);
100 self.preprocessors.push(arc_preprocessor);
101 self
102 }
103}
104
105impl<L: Literal, S: LiteralStorage<L>> Preprocessor<L, S> for PreprocessorChain<L, S> {
106 fn preprocess(&self, cnf: &[Clause<L, S>]) -> Vec<Clause<L, S>> {
120 self.preprocessors
121 .iter()
122 .fold(Vec::from(cnf), |current_cnf, preprocessor_arc| {
123 preprocessor_arc.preprocess(¤t_cnf)
124 })
125 }
126}
127
128#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
139pub struct PureLiteralElimination;
140
141impl PureLiteralElimination {
142 pub fn find_pures<L: Literal, S: LiteralStorage<L>>(cnf: &[Clause<L, S>]) -> FxHashSet<L> {
157 let mut pures = FxHashSet::default();
158 let mut impures = FxHashSet::default();
159
160 for clause in cnf {
161 for &lit in clause.iter() {
162 let var = lit.variable();
163 if impures.contains(&var) {
164 continue;
165 }
166
167 if pures.contains(&lit.negated()) {
168 pures.remove(&lit.negated());
169 impures.insert(var);
170 pures.remove(&lit);
171 } else if !impures.contains(&var) {
172 pures.insert(lit);
173 }
174 }
175 }
176
177 pures
178 }
179}
180
181impl<L: Literal, S: LiteralStorage<L>> Preprocessor<L, S> for PureLiteralElimination {
182 fn preprocess(&self, cnf: &[Clause<L, S>]) -> Vec<Clause<L, S>> {
190 let mut current_cnf = cnf.to_vec();
191
192 let pures = Self::find_pures(current_cnf.as_slice());
193
194 if pures.is_empty() {
195 return current_cnf;
196 }
197
198 current_cnf.retain(|clause| !clause.iter().any(|lit| pures.contains(lit)));
199
200 current_cnf
201 }
202}
203
204#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
212pub struct SubsumptionElimination;
213
214impl SubsumptionElimination {
215 fn is_subset_of<L: Literal, S: LiteralStorage<L>>(
223 shorter_clause: &Clause<L, S>,
224 longer_clause: &Clause<L, S>,
225 ) -> bool {
226 if shorter_clause.len() > longer_clause.len() {
227 return false; }
229 if shorter_clause.is_empty() {
230 return true; }
232
233 let mut shorter_iter = shorter_clause.iter();
234 let mut longer_iter = longer_clause.iter();
235
236 let mut current_shorter = shorter_iter.next();
237 let mut current_longer = longer_iter.next();
238
239 while let Some(s_lit) = current_shorter {
240 while let Some(l_lit) = current_longer {
241 if l_lit < s_lit {
242 current_longer = longer_iter.next();
243 } else {
244 break;
245 }
246 }
247
248 match current_longer {
249 Some(l_lit) if l_lit == s_lit => {
250 current_shorter = shorter_iter.next();
251 current_longer = longer_iter.next();
252 }
253 _ => {
254 return false;
255 }
256 }
257 }
258 true
259 }
260}
261
262impl<L: Literal, S: LiteralStorage<L>> Preprocessor<L, S> for SubsumptionElimination {
263 fn preprocess(&self, cnf: &[Clause<L, S>]) -> Vec<Clause<L, S>> {
269 let result = cnf.to_vec();
270 if result.len() < 2 {
271 return result;
272 }
273
274 let mut sorted_indices: Vec<usize> = (0..result.len()).collect();
275 sorted_indices.sort_by_key(|&i| result[i].len());
276
277 let mut removed_flags = vec![false; result.len()];
278 let mut num_removed = 0;
279
280 for i in 0..sorted_indices.len() {
281 let idx_i = sorted_indices[i];
282 if removed_flags[idx_i] {
283 continue;
284 }
285
286 for &idx_j in sorted_indices.iter().skip(i + 1) {
287 if removed_flags[idx_j] {
288 continue;
289 }
290
291 if Self::is_subset_of(&result[idx_i], &result[idx_j]) {
292 removed_flags[idx_j] = true;
293 num_removed += 1;
294 }
295 }
296 }
297
298 if num_removed == 0 {
299 return result;
300 }
301
302 let mut final_clauses = Vec::with_capacity(result.len() - num_removed);
303 for i in 0..result.len() {
304 if !removed_flags[i] {
305 final_clauses.push(result[i].clone());
306 }
307 }
308 final_clauses
309 }
310}
311
312#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
318pub struct TautologyElimination;
319
320impl<L: Literal, S: LiteralStorage<L>> Preprocessor<L, S> for TautologyElimination {
321 fn preprocess(&self, cnf: &[Clause<L, S>]) -> Vec<Clause<L, S>> {
325 cnf.iter()
326 .filter(|clause| !clause.is_tautology())
327 .cloned()
328 .collect()
329 }
330}
331
332#[cfg(test)]
333mod tests {
334 use super::*;
335 use crate::sat::literal::PackedLiteral;
336 use smallvec::SmallVec;
337
338 type TestLiteral = PackedLiteral;
339 type TestClauseStorage = SmallVec<[TestLiteral; 8]>;
340 type TestClause = Clause<TestLiteral, TestClauseStorage>;
341
342 fn lit(val: i32) -> TestLiteral {
343 TestLiteral::from_i32(val)
344 }
345 fn clause(lits_dimacs: &[i32]) -> TestClause {
346 lits_dimacs.iter().map(|&x| lit(x)).collect()
347 }
348 fn clause_sorted(lits_dimacs: &[i32]) -> TestClause {
349 let mut l: Vec<TestLiteral> = lits_dimacs.iter().map(|&x| lit(x)).collect();
350 l.sort();
351 Clause::new(&l)
352 }
353
354 #[test]
355 fn test_tautology_elimination() {
356 let preprocessor = TautologyElimination;
357 let cnf = vec![clause(&[1, -1, 2]), clause(&[1, 2]), clause(&[-2, 3, 2])];
358 let processed_cnf = preprocessor.preprocess(&cnf);
359 assert_eq!(processed_cnf.len(), 1);
360 assert_eq!(processed_cnf[0], clause(&[1, 2]));
361 }
362
363 #[test]
364 fn test_pure_literal_elimination() {
365 let preprocessor = PureLiteralElimination;
366 let cnf = vec![clause(&[1, 2]), clause(&[-2, -3]), clause(&[2, -2])];
367 let processed_cnf = preprocessor.preprocess(&cnf);
368 assert_eq!(processed_cnf.len(), 1);
369 assert_eq!(processed_cnf[0], clause(&[2, -2]));
370
371 let cnf_no_pures = vec![clause(&[1, 2]), clause(&[-1, -2])];
372 let processed_no_pures = preprocessor.preprocess(&cnf_no_pures);
373 assert_eq!(processed_no_pures.len(), 2);
374
375 let cnf_iterative = vec![clause(&[1]), clause(&[1, -2]), clause(&[2])];
376 let processed_iterative_pass1 = preprocessor.preprocess(&cnf_iterative);
377 assert_eq!(processed_iterative_pass1.len(), 1);
378 assert_eq!(processed_iterative_pass1[0], clause(&[2]));
379
380 let processed_iterative_pass2 = preprocessor.preprocess(&processed_iterative_pass1);
381 assert_eq!(processed_iterative_pass2.len(), 0);
382 }
383
384 #[test]
385 fn test_subsumption_elimination() {
386 let preprocessor = SubsumptionElimination;
387 let cnf = vec![
388 clause_sorted(&[1, 2]),
389 clause_sorted(&[1, 2, 3]),
390 clause_sorted(&[1, 3]),
391 clause_sorted(&[1]),
392 clause_sorted(&[4, 5]),
393 ];
394 let processed_cnf = preprocessor.preprocess(&cnf);
395
396 let expected_cnf = [clause_sorted(&[1]), clause_sorted(&[4, 5])];
397 let processed_set: FxHashSet<_> = processed_cnf.iter().cloned().collect();
398 let expected_set: FxHashSet<_> = expected_cnf.iter().cloned().collect();
399 assert_eq!(processed_set, expected_set);
400 assert_eq!(processed_cnf.len(), 2);
401 }
402
403 #[test]
404 fn test_preprocessor_chain() {
405 let cnf_initial = vec![clause(&[1, -1, 2]), clause(&[1, 2, 3]), clause(&[1, 2])];
406
407 let chain = PreprocessorChain::new()
408 .add_preprocessor(TautologyElimination)
409 .add_preprocessor(PureLiteralElimination)
410 .add_preprocessor(SubsumptionElimination);
411
412 let processed_cnf = chain.preprocess(&cnf_initial);
413 assert_eq!(processed_cnf.len(), 0);
414 }
415}