1#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
2use crate::sat::assignment::Assignment;
22use crate::sat::clause::Clause;
23use crate::sat::clause_storage::LiteralStorage;
24use crate::sat::cnf::Cnf;
25use crate::sat::literal::Literal;
26use crate::sat::literal::Variable;
27use crate::sat::preprocessing::PureLiteralElimination;
28use crate::sat::trail::{Reason, Trail};
29use clap::ValueEnum;
30use itertools::Itertools;
31use smallvec::SmallVec;
32use std::fmt::{Debug, Display};
33use std::marker::PhantomData;
34use std::ops::{Index, IndexMut};
35
36pub trait Propagator<L: Literal, S: LiteralStorage<L>, A: Assignment>: Debug + Clone {
47 fn new(cnf: &Cnf<L, S>) -> Self;
53
54 fn add_clause(&mut self, clause: &Clause<L, S>, idx: usize);
62
63 fn propagate(
80 &mut self,
81 trail: &mut Trail<L, S>,
82 assignment: &mut A,
83 cnf: &mut Cnf<L, S>,
84 ) -> Option<usize>;
85
86 fn num_propagations(&self) -> usize;
88
89 fn add_propagation(lit: L, clause_idx: usize, trail: &mut Trail<L, S>) {
100 trail.push(lit, trail.decision_level(), Reason::Clause(clause_idx));
101 }
102
103 fn cleanup_learnt(&mut self, learnt_idx: usize);
114}
115
116#[derive(Debug, Clone, PartialEq, Eq, Default)]
118pub struct WatchedLiterals<L: Literal, S: LiteralStorage<L>, A: Assignment, const N: usize = 8> {
119 watches: Vec<SmallVec<[usize; N]>>,
120 num_propagations: usize,
121 marker: PhantomData<*const (L, S, A)>,
122}
123
124impl<L: Literal, S: LiteralStorage<L> + Debug, A: Assignment, const N: usize> Propagator<L, S, A>
125 for WatchedLiterals<L, S, A, N>
126{
127 fn new(cnf: &Cnf<L, S>) -> Self {
128 let num_literal_indices = if cnf.num_vars == 0 {
129 0
130 } else {
131 cnf.num_vars * 2
132 };
133 let st = vec![SmallVec::new(); num_literal_indices];
134
135 let mut wl = Self {
136 watches: st,
137 num_propagations: 0,
138 marker: PhantomData,
139 };
140
141 for (i, clause) in cnf
142 .iter()
143 .enumerate()
144 .filter(|(_, c)| c.len() >= 2 && !c.is_unit() && !c.is_deleted())
145 {
146 wl.add_clause(clause, i);
147 }
148 wl
149 }
150
151 fn add_clause(&mut self, clause: &Clause<L, S>, idx: usize) {
152 if clause.len() < 2 || clause.is_deleted() {
153 return;
154 }
155 let a = clause[0];
156 let b = clause[1];
157 debug_assert_ne!(
158 a.variable(),
159 b.variable(),
160 "Clause {idx}: Attempting to watch two \
161 literals of the same variable: {a:?} and {b:?}"
162 );
163
164 self[a].push(idx);
165 self[b].push(idx);
166 }
167
168 fn propagate(
169 &mut self,
170 trail: &mut Trail<L, S>,
171 assignment: &mut A,
172 cnf: &mut Cnf<L, S>,
173 ) -> Option<usize> {
174 while trail.curr_idx < trail.len() {
175 let lit = trail[trail.curr_idx].lit;
176 trail.curr_idx = trail.curr_idx.wrapping_add(1);
177 assignment.assign(lit);
178 self.num_propagations = self.num_propagations.wrapping_add(1);
179
180 let watch_list_clone = self[lit.negated().index()].clone();
181 if let Some(idx) = self.propagate_watch(&watch_list_clone, trail, assignment, cnf) {
182 return Some(idx);
183 }
184 }
185 None
186 }
187
188 fn num_propagations(&self) -> usize {
189 self.num_propagations
190 }
191
192 fn cleanup_learnt(&mut self, learnt_idx: usize) {
193 for i in &mut self.watches {
194 i.retain(|&mut j| j < learnt_idx);
195 }
196 }
197}
198
199impl<L: Literal, S: LiteralStorage<L> + Debug, A: Assignment, const N: usize>
200 WatchedLiterals<L, S, A, N>
201{
202 pub fn propagate_watch(
217 &mut self,
218 indices: &[usize],
219 trail: &mut Trail<L, S>,
220 assignment: &A,
221 cnf: &mut Cnf<L, S>,
222 ) -> Option<usize> {
223 indices
224 .iter()
225 .find_map(|&idx| self.process_clause(idx, trail, assignment, cnf))
226 }
227
228 pub fn process_clause(
249 &mut self,
250 clause_idx: usize,
251 trail: &mut Trail<L, S>,
252 assignment: &A,
253 cnf: &mut Cnf<L, S>,
254 ) -> Option<usize> {
255 let clause = &mut cnf[clause_idx];
256
257 let first = clause[0];
258 let second = clause[1];
259
260 let first_value = assignment.literal_value(first);
261
262 if first_value == Some(true) {
263 return None;
264 }
265
266 let second_value = assignment.literal_value(second);
267
268 match (first_value, second_value) {
269 (Some(false), Some(false)) => {
270 debug_assert!(
271 clause
272 .iter()
273 .all(|&l| assignment.literal_value(l) == Some(false)),
274 "Clause: {clause:?} is not false, Values: {:?}, idx: {clause_idx}, trail: \
275 {:?}",
276 clause
277 .iter()
278 .map(|&l| assignment.literal_value(l))
279 .collect_vec(),
280 trail,
281 );
282 Some(clause_idx)
283 }
284 (None, Some(false)) => {
285 self.handle_false(first, clause_idx, assignment, cnf, trail);
286 None
287 }
288 (Some(false), None) => {
289 clause.swap(0, 1);
290 self.handle_false(second, clause_idx, assignment, cnf, trail);
291 None
292 }
293 (_, Some(true)) => {
294 clause.swap(0, 1);
295 None
296 }
297 (Some(true), _) | (None, None) => None,
298 }
299 }
300
301 fn handle_false(
302 &mut self,
303 other_lit: L,
304 clause_idx: usize,
305 assignment: &A,
306 cnf: &mut Cnf<L, S>,
307 trail: &mut Trail<L, S>,
308 ) {
309 if let Some(new_lit_idx) = Self::find_new_watch(clause_idx, cnf, assignment) {
310 self.handle_new_watch(clause_idx, new_lit_idx, cnf);
311 } else {
312 debug_assert!(
313 assignment.literal_value(other_lit).is_none(),
314 "In handle_false, \
315 other_lit ({other_lit:?}) was expected to be unassigned but is {:?}. Clause idx \
316 {clause_idx}",
317 assignment.literal_value(other_lit)
318 );
319 Self::add_propagation(other_lit, clause_idx, trail);
320 }
321 }
322
323 fn find_new_watch(clause_idx: usize, cnf: &Cnf<L, S>, assignment: &A) -> Option<usize> {
324 let clause = &cnf[clause_idx];
325 clause
326 .iter()
327 .skip(2)
328 .position(|&l| assignment.literal_value(l) != Some(false))
329 .map(|i| i.wrapping_add(2))
330 }
331
332 fn handle_new_watch(&mut self, clause_idx: usize, new_lit_idx: usize, cnf: &mut Cnf<L, S>) {
333 debug_assert!(new_lit_idx >= 2);
334
335 let new_lit = cnf[clause_idx][new_lit_idx];
336 let prev_lit = cnf[clause_idx][1];
337
338 cnf[clause_idx].swap(1, new_lit_idx);
339
340 self[new_lit].push(clause_idx);
341
342 if let Some(pos) = self[prev_lit].iter().position(|&i| i == clause_idx) {
343 self[prev_lit].swap_remove(pos);
344 }
345 }
346}
347
348impl<L: Literal, S: LiteralStorage<L>, A: Assignment, const N: usize> Index<usize>
349 for WatchedLiterals<L, S, A, N>
350{
351 type Output = SmallVec<[usize; N]>;
352 #[inline]
353 fn index(&self, index: usize) -> &Self::Output {
354 unsafe { self.watches.get_unchecked(index) }
355 }
356}
357
358impl<L: Literal, S: LiteralStorage<L>, A: Assignment, const N: usize> IndexMut<usize>
359 for WatchedLiterals<L, S, A, N>
360{
361 #[inline]
362 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
363 unsafe { self.watches.get_unchecked_mut(index) }
364 }
365}
366
367impl<L: Literal, S: LiteralStorage<L>, A: Assignment, const N: usize> Index<Variable>
368 for WatchedLiterals<L, S, A, N>
369{
370 type Output = SmallVec<[usize; N]>;
371 #[inline]
372 fn index(&self, index: Variable) -> &Self::Output {
373 &self[index as usize]
374 }
375}
376
377impl<L: Literal, S: LiteralStorage<L>, A: Assignment, const N: usize> IndexMut<Variable>
378 for WatchedLiterals<L, S, A, N>
379{
380 #[inline]
381 fn index_mut(&mut self, index: Variable) -> &mut Self::Output {
382 &mut self[index as usize]
383 }
384}
385
386impl<L: Literal, S: LiteralStorage<L>, A: Assignment, const N: usize> Index<L>
387 for WatchedLiterals<L, S, A, N>
388{
389 type Output = SmallVec<[usize; N]>;
390 #[inline]
391 fn index(&self, index: L) -> &Self::Output {
392 &self[index.index()]
393 }
394}
395
396impl<L: Literal, S: LiteralStorage<L>, A: Assignment, const N: usize> IndexMut<L>
397 for WatchedLiterals<L, S, A, N>
398{
399 #[inline]
400 fn index_mut(&mut self, index: L) -> &mut Self::Output {
401 &mut self[index.index()]
402 }
403}
404
405#[derive(Debug, Clone, PartialEq, Eq, Default)]
407pub struct UnitSearch<L: Literal, S: LiteralStorage<L>, A: Assignment>(
408 usize, PhantomData<(L, S, A)>,
410);
411
412impl<L: Literal, S: LiteralStorage<L>, A: Assignment> Propagator<L, S, A> for UnitSearch<L, S, A> {
413 fn new(_: &Cnf<L, S>) -> Self {
414 Self(0, PhantomData)
415 }
416
417 fn add_clause(&mut self, _: &Clause<L, S>, _: usize) {}
418
419 fn propagate(
420 &mut self,
421 trail: &mut Trail<L, S>,
422 assignment: &mut A,
423 cnf: &mut Cnf<L, S>,
424 ) -> Option<usize> {
425 loop {
426 let mut new_propagations_this_scan = false;
427 while trail.curr_idx < trail.len() {
428 let lit = trail[trail.curr_idx].lit;
429 trail.curr_idx = trail.curr_idx.wrapping_add(1);
430 assignment.assign(lit);
431 self.0 = self.0.wrapping_add(1);
432 }
433
434 match Self::process_cnf_scan(trail, assignment, cnf) {
435 Some(ScanResult::Conflict(idx)) => return Some(idx),
436 Some(ScanResult::Propagated) => new_propagations_this_scan = true,
437 None => {}
438 }
439
440 if !new_propagations_this_scan && trail.curr_idx == trail.len() {
441 return None;
442 }
443 }
444 }
445
446 fn num_propagations(&self) -> usize {
447 self.0
448 }
449
450 fn cleanup_learnt(&mut self, _: usize) {}
451}
452
453enum UnitSearchResult<L: Literal> {
454 Unsat(usize),
455 Unit(L),
456 Continue,
457}
458
459enum ScanResult {
460 Conflict(usize),
461 Propagated,
462}
463
464impl<L: Literal, S: LiteralStorage<L>, A: Assignment> UnitSearch<L, S, A> {
465 fn process_cnf_scan(
466 trail: &mut Trail<L, S>,
467 assignment: &A,
468 cnf: &Cnf<L, S>,
469 ) -> Option<ScanResult> {
470 let mut propagated_in_scan = false;
471 for (idx, clause) in cnf.iter().enumerate() {
472 if clause.is_deleted() {
473 continue;
474 }
475 match Self::process_clause_eval(clause, assignment, idx) {
476 UnitSearchResult::Unsat(idx) => return Some(ScanResult::Conflict(idx)),
477 UnitSearchResult::Unit(lit) => {
478 if assignment.var_value(lit.variable()).is_none() {
479 Self::add_propagation(lit, idx, trail);
480 propagated_in_scan = true;
481 }
482 }
483 UnitSearchResult::Continue => {}
484 }
485 }
486 if propagated_in_scan {
487 Some(ScanResult::Propagated)
488 } else {
489 None
490 }
491 }
492
493 fn process_clause_eval(
494 clause: &Clause<L, S>,
495 assignment: &A,
496 idx: usize,
497 ) -> UnitSearchResult<L> {
498 let mut unassigned_lit_opt = None;
499 let mut num_unassigned = 0;
500
501 for &lit in clause.iter() {
502 match assignment.literal_value(lit) {
503 Some(true) => return UnitSearchResult::Continue,
504 Some(false) => {}
505 None => {
506 num_unassigned += 1;
507 if unassigned_lit_opt.is_none() {
508 unassigned_lit_opt = Some(lit);
509 }
510 if num_unassigned > 1 {
511 return UnitSearchResult::Continue;
512 }
513 }
514 }
515 }
516
517 if num_unassigned == 1 {
518 UnitSearchResult::Unit(
519 unassigned_lit_opt
520 .expect("num_unassigned is 1, so unassigned_lit_opt must be Some"),
521 )
522 } else if num_unassigned == 0 {
523 UnitSearchResult::Unsat(idx)
524 } else {
525 UnitSearchResult::Continue
526 }
527 }
528}
529
530#[derive(Debug, Clone, PartialEq, Eq, Default)]
532pub struct UnitPropagationWithPureLiterals<L: Literal, S: LiteralStorage<L>, A: Assignment>(
533 UnitSearch<L, S, A>,
534);
535
536impl<L: Literal, S: LiteralStorage<L>, A: Assignment> Propagator<L, S, A>
537 for UnitPropagationWithPureLiterals<L, S, A>
538{
539 fn new(cnf: &Cnf<L, S>) -> Self {
540 Self(UnitSearch::new(cnf))
541 }
542
543 fn add_clause(&mut self, clause: &Clause<L, S>, idx: usize) {
544 self.0.add_clause(clause, idx);
545 }
546
547 fn propagate(
548 &mut self,
549 trail: &mut Trail<L, S>,
550 assignment: &mut A,
551 cnf: &mut Cnf<L, S>,
552 ) -> Option<usize> {
553 loop {
554 if let Some(idx) = self.0.propagate(trail, assignment, cnf) {
555 return Some(idx);
556 }
557
558 let trail_len_before_pures = trail.len();
559 let curr_idx_before_pures = trail.curr_idx;
560
561 Self::find_pure_literals_assign(trail, assignment, cnf);
562
563 if curr_idx_before_pures == trail.len() && trail_len_before_pures == trail.len() {
564 return None;
565 }
566 }
567 }
568
569 fn num_propagations(&self) -> usize {
570 self.0.num_propagations()
571 }
572
573 fn cleanup_learnt(&mut self, learnt_idx: usize) {
574 self.0.cleanup_learnt(learnt_idx);
575 }
576}
577
578impl<L: Literal, S: LiteralStorage<L>, A: Assignment> UnitPropagationWithPureLiterals<L, S, A> {
579 fn find_pure_literals_assign(trail: &mut Trail<L, S>, assignment: &A, cnf: &Cnf<L, S>) {
580 let pures = PureLiteralElimination::find_pures(&cnf.clauses);
581 for &lit in &pures {
582 if assignment.var_value(lit.variable()).is_none() {
583 Self::add_propagation(lit, 0, trail);
584 }
585 }
586 }
587}
588
589#[derive(Debug, Clone, PartialEq, Eq)]
591pub enum PropagatorImpls<L: Literal, S: LiteralStorage<L>, A: Assignment> {
592 WatchedLiterals(WatchedLiterals<L, S, A>),
594 UnitSearch(UnitSearch<L, S, A>),
596 UnitPropagationWithPureLiterals(UnitPropagationWithPureLiterals<L, S, A>),
598}
599
600impl<L: Literal, S: LiteralStorage<L>, A: Assignment> Propagator<L, S, A>
601 for PropagatorImpls<L, S, A>
602{
603 fn new(cnf: &Cnf<L, S>) -> Self {
604 Self::WatchedLiterals(WatchedLiterals::new(cnf))
605 }
606
607 fn add_clause(&mut self, clause: &Clause<L, S>, idx: usize) {
608 match self {
609 Self::WatchedLiterals(wl) => wl.add_clause(clause, idx),
610 Self::UnitSearch(us) => us.add_clause(clause, idx),
611 Self::UnitPropagationWithPureLiterals(up) => up.add_clause(clause, idx),
612 }
613 }
614
615 fn propagate(
616 &mut self,
617 trail: &mut Trail<L, S>,
618 assignment: &mut A,
619 cnf: &mut Cnf<L, S>,
620 ) -> Option<usize> {
621 match self {
622 Self::WatchedLiterals(wl) => wl.propagate(trail, assignment, cnf),
623 Self::UnitSearch(us) => us.propagate(trail, assignment, cnf),
624 Self::UnitPropagationWithPureLiterals(up) => up.propagate(trail, assignment, cnf),
625 }
626 }
627
628 fn num_propagations(&self) -> usize {
629 match self {
630 Self::WatchedLiterals(wl) => wl.num_propagations(),
631 Self::UnitSearch(us) => us.num_propagations(),
632 Self::UnitPropagationWithPureLiterals(up) => up.num_propagations(),
633 }
634 }
635
636 fn cleanup_learnt(&mut self, learnt_idx: usize) {
637 match self {
638 Self::WatchedLiterals(wl) => wl.cleanup_learnt(learnt_idx),
639 Self::UnitSearch(us) => us.cleanup_learnt(learnt_idx),
640 Self::UnitPropagationWithPureLiterals(up) => up.cleanup_learnt(learnt_idx),
641 }
642 }
643}
644
645#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash, Default, ValueEnum)]
647pub enum PropagatorType {
648 #[default]
650 WatchedLiterals,
651 UnitSearch,
653 UnitPropagationWithPureLiterals,
655}
656
657impl Display for PropagatorType {
658 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
659 match self {
660 Self::WatchedLiterals => write!(f, "watched-literals"),
661 Self::UnitSearch => write!(f, "unit-search"),
662 Self::UnitPropagationWithPureLiterals => {
663 write!(f, "unit-propagation-with-pure-literals")
664 }
665 }
666 }
667}
668
669impl PropagatorType {
670 #[must_use]
672 pub fn to_impl<L: Literal, S: LiteralStorage<L>, A: Assignment>(
673 self,
674 cnf: &Cnf<L, S>,
675 ) -> PropagatorImpls<L, S, A> {
676 match self {
677 Self::WatchedLiterals => PropagatorImpls::WatchedLiterals(WatchedLiterals::new(cnf)),
678 Self::UnitSearch => PropagatorImpls::UnitSearch(UnitSearch::new(cnf)),
679 Self::UnitPropagationWithPureLiterals => {
680 PropagatorImpls::UnitPropagationWithPureLiterals(
681 UnitPropagationWithPureLiterals::new(cnf),
682 )
683 }
684 }
685 }
686}
687
688#[cfg(test)]
689mod tests {
690 use super::*;
691 use crate::sat::assignment::VecAssignment;
692 use crate::sat::literal::PackedLiteral;
693
694 type TestLiteral = PackedLiteral;
695 type TestClauseStorage = SmallVec<[TestLiteral; 8]>;
696 type TestCnf = Cnf<TestLiteral, TestClauseStorage>;
697 type TestAssignment = VecAssignment;
698 type TestTrail = Trail<TestLiteral, TestClauseStorage>;
699
700 fn lit(val: i32) -> TestLiteral {
701 TestLiteral::from_i32(val)
702 }
703
704 fn setup_test_environment(
705 clauses_dimacs: Vec<Vec<i32>>,
706 num_total_vars: usize,
707 ) -> (TestCnf, TestTrail, TestAssignment) {
708 let mut cnf = TestCnf::new(clauses_dimacs);
709 let effective_num_vars = cnf.num_vars.max(num_total_vars);
710 cnf.num_vars = effective_num_vars;
711
712 let trail = TestTrail::new(&cnf.clauses, effective_num_vars);
713 let assignment = TestAssignment::new(effective_num_vars);
714 (cnf, trail, assignment)
715 }
716
717 #[test]
718 fn test_watched_literals_new_and_add_clause() {
719 let (cnf, _trail, _assignment) =
720 setup_test_environment(vec![vec![1, 2, -3], vec![-1, 4]], 5);
721 let propagator = WatchedLiterals::<_, _, TestAssignment>::new(&cnf);
722
723 assert_eq!(propagator.watches.len(), cnf.num_vars * 2);
724
725 assert!(propagator[lit(1)].contains(&0));
726 assert!(propagator[lit(2)].contains(&0));
727 assert!(!propagator[lit(-3)].contains(&0));
728
729 assert!(propagator[lit(-1)].contains(&1));
730 assert!(propagator[lit(4)].contains(&1));
731 }
732
733 #[test]
734 fn test_watched_literals_simple_propagation() {
735 let (mut cnf, mut trail, mut assignment) =
736 setup_test_environment(vec![vec![-1, 2], vec![-2, 3]], 4);
737 let mut propagator = WatchedLiterals::<_, _, TestAssignment>::new(&cnf);
738
739 trail.push(lit(1), 1, Reason::Decision);
740
741 let conflict = propagator.propagate(&mut trail, &mut assignment, &mut cnf);
742 assert!(conflict.is_none());
743
744 assert_eq!(trail.len(), 3);
745 assert_eq!(trail.t[0].lit, lit(1));
746 assert_eq!(trail.t[1].lit, lit(2));
747 assert_eq!(trail.t[1].reason, Reason::Clause(0));
748 assert_eq!(trail.t[2].lit, lit(3));
749 assert_eq!(trail.t[2].reason, Reason::Clause(1));
750
751 assert_eq!(assignment.literal_value(lit(1)), Some(true));
752 assert_eq!(assignment.literal_value(lit(2)), Some(true));
753 assert_eq!(assignment.literal_value(lit(3)), Some(true));
754 assert_eq!(propagator.num_propagations(), 3);
755 }
756
757 #[test]
758 fn test_watched_literals_conflict() {
759 let (mut cnf, mut trail, mut assignment) =
760 setup_test_environment(vec![vec![-1, 2], vec![-1, -2]], 3);
761 let mut propagator = WatchedLiterals::<_, _, TestAssignment>::new(&cnf);
762
763 trail.push(lit(1), 1, Reason::Decision);
764
765 let conflict_idx = propagator.propagate(&mut trail, &mut assignment, &mut cnf);
766 assert!(conflict_idx.is_some());
767 assert_eq!(conflict_idx.unwrap(), 1);
768 }
769
770 #[test]
771 fn test_watched_literals_find_new_watch() {
772 let (mut cnf, mut trail, mut assignment) =
773 setup_test_environment(vec![vec![-1, 2, 3, -4]], 5);
774 let mut propagator = WatchedLiterals::<_, _, TestAssignment>::new(&cnf);
775
776 trail.push(lit(1), 1, Reason::Decision);
777
778 let conflict = propagator.propagate(&mut trail, &mut assignment, &mut cnf);
779 assert!(conflict.is_none());
780
781 assert!(propagator[lit(2)].contains(&0));
782 assert!(propagator[lit(3)].contains(&0));
783 assert!(!propagator[lit(-1)].contains(&0));
784 assert!(!propagator[lit(-4)].contains(&0));
785 }
786
787 #[test]
788 fn test_unit_search_simple_propagation() {
789 let (mut cnf, mut trail, mut assignment) =
790 setup_test_environment(vec![vec![-1, 2], vec![-2, 3]], 4);
791 let mut propagator = UnitSearch::new(&cnf);
792
793 trail.push(lit(1), 1, Reason::Decision);
794 let conflict = propagator.propagate(&mut trail, &mut assignment, &mut cnf);
795
796 assert!(conflict.is_none());
797 assert_eq!(trail.len(), 3);
798 assert_eq!(trail.t[0].lit, lit(1));
799 assert_eq!(trail.t[1].lit, lit(2));
800 assert_eq!(trail.t[2].lit, lit(3));
801 assert_eq!(assignment.var_value(lit(3).variable()), Some(true));
802 assert_eq!(propagator.num_propagations(), 3);
803 }
804
805 #[test]
806 fn test_unit_search_conflict() {
807 let (mut cnf, mut trail, mut assignment) =
808 setup_test_environment(vec![vec![-1, 2], vec![-1, -2]], 3);
809 let mut propagator = UnitSearch::new(&cnf);
810 trail.push(lit(1), 1, Reason::Decision);
811 let conflict_idx = propagator.propagate(&mut trail, &mut assignment, &mut cnf);
812 assert!(conflict_idx.is_some());
813 assert_eq!(conflict_idx.unwrap(), 1);
814 }
815
816 #[test]
817 fn test_unit_prop_with_pures() {
818 let (mut cnf, mut trail, mut assignment) =
819 setup_test_environment(vec![vec![-1, 2], vec![3]], 4);
820 let mut propagator = UnitPropagationWithPureLiterals::new(&cnf);
821
822 trail.push(lit(1), 1, Reason::Decision);
823 let conflict = propagator.propagate(&mut trail, &mut assignment, &mut cnf);
824
825 assert!(conflict.is_none());
826 assert_eq!(trail.len(), 3);
827
828 assert_eq!(assignment.var_value(lit(3).variable()), Some(true));
829 }
830
831 #[test]
832 fn test_watched_literals_empty_cnf() {
833 let (cnf, _trail, _assignment) = setup_test_environment(vec![], 1);
834 let propagator = WatchedLiterals::<_, _, TestAssignment>::new(&cnf);
835 assert_eq!(propagator.watches.len(), 2);
836 }
837
838 #[test]
839 fn test_watched_literals_unit_clause_in_cnf() {
840 let (cnf, _trail, _assignment) = setup_test_environment(vec![vec![1], vec![-2, 3]], 4);
841 let propagator = WatchedLiterals::<_, _, TestAssignment>::new(&cnf);
842 assert!(!propagator[lit(1)].contains(&0));
843 assert!(propagator[lit(-2)].contains(&1));
844 assert!(propagator[lit(3)].contains(&1));
845 }
846}