1use crate::chb::CHB;
4use crate::chrono::ChronoBacktrack;
5use crate::clause::{ClauseDatabase, ClauseId};
6use crate::literal::{LBool, Lit, Var};
7use crate::lrb::LRB;
8use crate::trail::{Reason, Trail};
9use crate::vsids::VSIDS;
10use crate::watched::{WatchLists, Watcher};
11use smallvec::SmallVec;
12
13#[derive(Debug, Clone)]
17struct BinaryImplicationGraph {
18 implications: Vec<Vec<(Lit, ClauseId)>>,
20}
21
22impl BinaryImplicationGraph {
23 fn new(num_vars: usize) -> Self {
24 Self {
25 implications: vec![Vec::new(); num_vars * 2],
26 }
27 }
28
29 fn resize(&mut self, num_vars: usize) {
30 self.implications.resize(num_vars * 2, Vec::new());
31 }
32
33 fn add(&mut self, lit: Lit, implied: Lit, clause_id: ClauseId) {
34 self.implications[lit.code() as usize].push((implied, clause_id));
35 }
36
37 fn get(&self, lit: Lit) -> &[(Lit, ClauseId)] {
38 &self.implications[lit.code() as usize]
39 }
40
41 fn clear(&mut self) {
42 for implications in &mut self.implications {
43 implications.clear();
44 }
45 }
46}
47
48#[derive(Debug, Clone)]
50pub enum TheoryCheckResult {
51 Sat,
53 Conflict(SmallVec<[Lit; 8]>),
55 Propagated(Vec<(Lit, SmallVec<[Lit; 8]>)>),
57}
58
59pub trait TheoryCallback {
62 fn on_assignment(&mut self, lit: Lit) -> TheoryCheckResult;
65
66 fn final_check(&mut self) -> TheoryCheckResult;
68
69 fn on_backtrack(&mut self, level: u32);
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75pub enum SolverResult {
76 Sat,
78 Unsat,
80 Unknown,
82}
83
84#[derive(Debug, Clone)]
86pub struct SolverConfig {
87 pub restart_interval: u64,
89 pub restart_multiplier: f64,
91 pub clause_deletion_threshold: usize,
93 pub var_decay: f64,
95 pub clause_decay: f64,
97 pub random_polarity_prob: f64,
99 pub restart_strategy: RestartStrategy,
101 pub enable_lazy_hyper_binary: bool,
103 pub use_chb_branching: bool,
105 pub use_lrb_branching: bool,
107 pub enable_inprocessing: bool,
109 pub inprocessing_interval: u64,
111 pub enable_chronological_backtrack: bool,
113 pub chrono_backtrack_threshold: u32,
115}
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq)]
119pub enum RestartStrategy {
120 Luby,
122 Geometric,
124 Glucose,
126 LocalLbd,
128}
129
130impl Default for SolverConfig {
131 fn default() -> Self {
132 Self {
133 restart_interval: 100,
134 restart_multiplier: 1.5,
135 clause_deletion_threshold: 10000,
136 var_decay: 0.95,
137 clause_decay: 0.999,
138 random_polarity_prob: 0.02,
139 restart_strategy: RestartStrategy::Luby,
140 enable_lazy_hyper_binary: true,
141 use_chb_branching: false,
142 use_lrb_branching: false,
143 enable_inprocessing: false,
144 inprocessing_interval: 5000,
145 enable_chronological_backtrack: true,
146 chrono_backtrack_threshold: 100,
147 }
148 }
149}
150
151#[derive(Debug, Default, Clone)]
153pub struct SolverStats {
154 pub decisions: u64,
156 pub propagations: u64,
158 pub conflicts: u64,
160 pub restarts: u64,
162 pub learned_clauses: u64,
164 pub deleted_clauses: u64,
166 pub binary_clauses: u64,
168 pub unit_clauses: u64,
170 pub total_lbd: u64,
172 pub minimizations: u64,
174 pub literals_removed: u64,
176 pub chrono_backtracks: u64,
178 pub non_chrono_backtracks: u64,
180}
181
182impl SolverStats {
183 #[must_use]
185 pub fn avg_lbd(&self) -> f64 {
186 if self.learned_clauses == 0 {
187 0.0
188 } else {
189 self.total_lbd as f64 / self.learned_clauses as f64
190 }
191 }
192
193 #[must_use]
195 pub fn avg_decisions_per_conflict(&self) -> f64 {
196 if self.conflicts == 0 {
197 0.0
198 } else {
199 self.decisions as f64 / self.conflicts as f64
200 }
201 }
202
203 #[must_use]
205 pub fn propagations_per_conflict(&self) -> f64 {
206 if self.conflicts == 0 {
207 0.0
208 } else {
209 self.propagations as f64 / self.conflicts as f64
210 }
211 }
212
213 #[must_use]
215 pub fn deletion_ratio(&self) -> f64 {
216 if self.learned_clauses == 0 {
217 0.0
218 } else {
219 self.deleted_clauses as f64 / self.learned_clauses as f64
220 }
221 }
222
223 #[must_use]
225 pub fn chrono_backtrack_ratio(&self) -> f64 {
226 let total = self.chrono_backtracks + self.non_chrono_backtracks;
227 if total == 0 {
228 0.0
229 } else {
230 self.chrono_backtracks as f64 / total as f64
231 }
232 }
233
234 pub fn display(&self) {
236 println!("========== Solver Statistics ==========");
237 println!("Decisions: {:>12}", self.decisions);
238 println!("Propagations: {:>12}", self.propagations);
239 println!("Conflicts: {:>12}", self.conflicts);
240 println!("Restarts: {:>12}", self.restarts);
241 println!("Learned clauses: {:>12}", self.learned_clauses);
242 println!(" - Unit clauses: {:>12}", self.unit_clauses);
243 println!(" - Binary clauses: {:>12}", self.binary_clauses);
244 println!("Deleted clauses: {:>12}", self.deleted_clauses);
245 println!("Minimizations: {:>12}", self.minimizations);
246 println!("Literals removed: {:>12}", self.literals_removed);
247 println!("Chrono backtracks: {:>12}", self.chrono_backtracks);
248 println!("Non-chrono backtracks: {:>12}", self.non_chrono_backtracks);
249 println!("---------------------------------------");
250 println!("Avg LBD: {:>12.2}", self.avg_lbd());
251 println!(
252 "Avg decisions/conflict: {:>12.2}",
253 self.avg_decisions_per_conflict()
254 );
255 println!(
256 "Propagations/conflict: {:>12.2}",
257 self.propagations_per_conflict()
258 );
259 println!(
260 "Deletion ratio: {:>12.2}%",
261 self.deletion_ratio() * 100.0
262 );
263 println!(
264 "Chrono backtrack ratio: {:>12.2}%",
265 self.chrono_backtrack_ratio() * 100.0
266 );
267 println!("=======================================");
268 }
269}
270
271#[derive(Debug)]
273pub struct Solver {
274 config: SolverConfig,
276 num_vars: usize,
278 clauses: ClauseDatabase,
280 trail: Trail,
282 watches: WatchLists,
284 vsids: VSIDS,
286 chb: CHB,
288 lrb: LRB,
290 stats: SolverStats,
292 learnt: SmallVec<[Lit; 16]>,
294 seen: Vec<bool>,
296 analyze_stack: Vec<Lit>,
298 restart_threshold: u64,
300 assertion_levels: Vec<usize>,
302 assertion_trail_sizes: Vec<usize>,
304 assertion_clause_ids: Vec<Vec<ClauseId>>,
306 model: Vec<LBool>,
308 trivially_unsat: bool,
310 phase: Vec<bool>,
312 luby_index: u64,
314 level_marks: Vec<u32>,
316 lbd_mark: u32,
318 learned_clause_ids: Vec<ClauseId>,
320 conflicts_since_deletion: u64,
322 rng_state: u64,
324 recent_lbd_sum: u64,
326 recent_lbd_count: u64,
328 binary_graph: BinaryImplicationGraph,
330 global_lbd_sum: u64,
332 global_lbd_count: u64,
334 conflicts_since_local_restart: u64,
336 conflicts_since_inprocessing: u64,
338 chrono_backtrack: ChronoBacktrack,
340 clause_bump_increment: f64,
342}
343
344impl Default for Solver {
345 fn default() -> Self {
346 Self::new()
347 }
348}
349
350impl Solver {
351 #[must_use]
353 pub fn new() -> Self {
354 Self::with_config(SolverConfig::default())
355 }
356
357 #[must_use]
359 pub fn with_config(config: SolverConfig) -> Self {
360 let chrono_enabled = config.enable_chronological_backtrack;
361 let chrono_threshold = config.chrono_backtrack_threshold;
362
363 Self {
364 restart_threshold: config.restart_interval,
365 config,
366 num_vars: 0,
367 clauses: ClauseDatabase::new(),
368 trail: Trail::new(0),
369 watches: WatchLists::new(0),
370 vsids: VSIDS::new(0),
371 chb: CHB::new(0),
372 lrb: LRB::new(0),
373 stats: SolverStats::default(),
374 learnt: SmallVec::new(),
375 seen: Vec::new(),
376 analyze_stack: Vec::new(),
377 assertion_levels: vec![0],
378 assertion_trail_sizes: vec![0],
379 assertion_clause_ids: vec![Vec::new()],
380 model: Vec::new(),
381 trivially_unsat: false,
382 phase: Vec::new(),
383 luby_index: 0,
384 level_marks: Vec::new(),
385 lbd_mark: 0,
386 learned_clause_ids: Vec::new(),
387 conflicts_since_deletion: 0,
388 rng_state: 0x853c_49e6_748f_ea9b, recent_lbd_sum: 0,
390 recent_lbd_count: 0,
391 binary_graph: BinaryImplicationGraph::new(0),
392 global_lbd_sum: 0,
393 global_lbd_count: 0,
394 conflicts_since_local_restart: 0,
395 conflicts_since_inprocessing: 0,
396 chrono_backtrack: ChronoBacktrack::new(chrono_enabled, chrono_threshold),
397 clause_bump_increment: 1.0,
398 }
399 }
400
401 pub fn new_var(&mut self) -> Var {
403 let var = Var::new(self.num_vars as u32);
404 self.num_vars += 1;
405 self.trail.resize(self.num_vars);
406 self.watches.resize(self.num_vars);
407 self.binary_graph.resize(self.num_vars);
408 self.vsids.insert(var);
409 self.chb.insert(var);
410 self.lrb.resize(self.num_vars);
411 self.seen.resize(self.num_vars, false);
412 self.model.resize(self.num_vars, LBool::Undef);
413 self.phase.resize(self.num_vars, false); if self.level_marks.len() < self.num_vars {
416 self.level_marks.resize(self.num_vars, 0);
417 }
418 var
419 }
420
421 pub fn ensure_vars(&mut self, n: usize) {
423 while self.num_vars < n {
424 self.new_var();
425 }
426 }
427
428 pub fn add_clause(&mut self, lits: impl IntoIterator<Item = Lit>) -> bool {
430 let mut clause_lits: SmallVec<[Lit; 8]> = lits.into_iter().collect();
431
432 for lit in &clause_lits {
434 let var_idx = lit.var().index();
435 if var_idx >= self.num_vars {
436 self.ensure_vars(var_idx + 1);
437 }
438 }
439
440 clause_lits.sort_by_key(|l| l.code());
442 clause_lits.dedup();
443
444 for i in 0..clause_lits.len() {
446 for j in (i + 1)..clause_lits.len() {
447 if clause_lits[i] == clause_lits[j].negate() {
448 return true; }
450 }
451 }
452
453 match clause_lits.len() {
455 0 => {
456 self.trivially_unsat = true;
457 return false; }
459 1 => {
460 let lit = clause_lits[0];
462 if self.trail.lit_value(lit).is_false() {
463 self.trivially_unsat = true;
464 return false;
465 }
466 if !self.trail.is_assigned(lit.var()) {
467 self.trail.assign_decision(lit);
468 }
469 return true;
470 }
471 2 => {
472 let clause_id = self.clauses.add_original(clause_lits.iter().copied());
474
475 if let Some(current_level_clauses) = self.assertion_clause_ids.last_mut() {
477 current_level_clauses.push(clause_id);
478 }
479
480 let lit0 = clause_lits[0];
481 let lit1 = clause_lits[1];
482
483 self.binary_graph.add(lit0.negate(), lit1, clause_id);
485 self.binary_graph.add(lit1.negate(), lit0, clause_id);
486
487 self.watches
489 .add(lit0.negate(), Watcher::new(clause_id, lit1));
490 self.watches
491 .add(lit1.negate(), Watcher::new(clause_id, lit0));
492
493 return true;
494 }
495 _ => {}
496 }
497
498 let clause_id = self.clauses.add_original(clause_lits.iter().copied());
500
501 if let Some(current_level_clauses) = self.assertion_clause_ids.last_mut() {
503 current_level_clauses.push(clause_id);
504 }
505
506 let lit0 = clause_lits[0];
508 let lit1 = clause_lits[1];
509
510 self.watches
511 .add(lit0.negate(), Watcher::new(clause_id, lit1));
512 self.watches
513 .add(lit1.negate(), Watcher::new(clause_id, lit0));
514
515 true
516 }
517
518 pub fn add_clause_dimacs(&mut self, lits: &[i32]) -> bool {
520 self.add_clause(lits.iter().map(|&l| Lit::from_dimacs(l)))
521 }
522
523 pub fn solve(&mut self) -> SolverResult {
525 if self.trivially_unsat {
527 return SolverResult::Unsat;
528 }
529
530 if self.propagate().is_some() {
532 return SolverResult::Unsat;
533 }
534
535 loop {
536 if let Some(conflict) = self.propagate() {
538 self.stats.conflicts += 1;
539 self.conflicts_since_inprocessing += 1;
540
541 if self.trail.decision_level() == 0 {
542 return SolverResult::Unsat;
543 }
544
545 let (backtrack_level, learnt_clause) = self.analyze(conflict);
547
548 self.backtrack_with_phase_saving(backtrack_level);
550
551 if learnt_clause.len() == 1 {
553 self.trail.assign_decision(learnt_clause[0]);
554 } else {
555 let lbd = self.compute_lbd(&learnt_clause);
557
558 self.recent_lbd_sum += u64::from(lbd);
560 self.recent_lbd_count += 1;
561 self.global_lbd_sum += u64::from(lbd);
562 self.global_lbd_count += 1;
563
564 if self.recent_lbd_count >= 5000 {
566 self.recent_lbd_sum /= 2;
567 self.recent_lbd_count /= 2;
568 }
569
570 let clause_id = self.clauses.add_learned(learnt_clause.iter().copied());
571 self.stats.learned_clauses += 1;
572
573 if let Some(clause) = self.clauses.get_mut(clause_id) {
575 clause.lbd = lbd;
576 }
577
578 self.learned_clause_ids.push(clause_id);
580
581 if let Some(current_level_clauses) = self.assertion_clause_ids.last_mut() {
583 current_level_clauses.push(clause_id);
584 }
585
586 let lit0 = learnt_clause[0];
588 let lit1 = learnt_clause[1];
589 self.watches
590 .add(lit0.negate(), Watcher::new(clause_id, lit1));
591 self.watches
592 .add(lit1.negate(), Watcher::new(clause_id, lit0));
593
594 self.trail.assign_propagation(learnt_clause[0], clause_id);
596 }
597
598 self.vsids.decay();
600 self.chb.decay();
601 self.lrb.decay();
602 self.lrb.on_conflict();
603 self.clauses.decay_activity(self.config.clause_decay);
604 self.clause_bump_increment /= self.config.clause_decay;
606
607 self.conflicts_since_deletion += 1;
609
610 if self.conflicts_since_deletion >= self.config.clause_deletion_threshold as u64 {
612 self.reduce_clause_database();
613 self.conflicts_since_deletion = 0;
614
615 if self.stats.restarts % 10 == 0 {
617 let saved_level = self.trail.decision_level();
618 if saved_level == 0 {
619 self.vivify_clauses();
620 }
621 }
622 }
623
624 if self.stats.conflicts >= self.restart_threshold {
626 self.restart();
627 }
628
629 if self.config.enable_inprocessing
631 && self.conflicts_since_inprocessing >= self.config.inprocessing_interval
632 {
633 self.inprocess();
634 self.conflicts_since_inprocessing = 0;
635 }
636 } else {
637 if let Some(var) = self.pick_branch_var() {
639 self.stats.decisions += 1;
640 self.trail.new_decision_level();
641
642 let polarity = if self.rand_bool(self.config.random_polarity_prob) {
644 self.rand_bool(0.5)
646 } else {
647 self.phase[var.index()]
649 };
650 let lit = if polarity {
651 Lit::pos(var)
652 } else {
653 Lit::neg(var)
654 };
655 self.trail.assign_decision(lit);
656 } else {
657 self.save_model();
659 return SolverResult::Sat;
660 }
661 }
662 }
663 }
664
665 pub fn solve_with_assumptions(
676 &mut self,
677 assumptions: &[Lit],
678 ) -> (SolverResult, Option<Vec<Lit>>) {
679 if self.trivially_unsat {
680 return (SolverResult::Unsat, Some(Vec::new()));
681 }
682
683 for &lit in assumptions {
685 while self.num_vars <= lit.var().index() {
686 self.new_var();
687 }
688 }
689
690 if self.propagate().is_some() {
692 return (SolverResult::Unsat, Some(Vec::new()));
693 }
694
695 let assumption_level_start = self.trail.decision_level();
697
698 for (i, &lit) in assumptions.iter().enumerate() {
700 let value = self.trail.lit_value(lit);
702 if value.is_true() {
703 continue; }
705 if value.is_false() {
706 let core = self.extract_assumption_core(assumptions, i);
708 self.backtrack(assumption_level_start);
709 return (SolverResult::Unsat, Some(core));
710 }
711
712 self.trail.new_decision_level();
714 self.trail.assign_decision(lit);
715
716 if let Some(_conflict) = self.propagate() {
718 let core = self.analyze_assumption_conflict(assumptions);
720 self.backtrack(assumption_level_start);
721 return (SolverResult::Unsat, Some(core));
722 }
723 }
724
725 loop {
727 if let Some(conflict) = self.propagate() {
728 self.stats.conflicts += 1;
729
730 let backtrack_level = self.analyze_conflict_level(conflict);
732
733 if backtrack_level <= assumption_level_start {
734 let core = self.analyze_assumption_conflict(assumptions);
736 self.backtrack(assumption_level_start);
737 return (SolverResult::Unsat, Some(core));
738 }
739
740 let (bt_level, learnt_clause) = self.analyze(conflict);
741 self.backtrack_with_phase_saving(bt_level.max(assumption_level_start + 1));
742 self.learn_clause(learnt_clause);
743
744 self.vsids.decay();
745 self.clauses.decay_activity(self.config.clause_decay);
746 self.handle_clause_deletion_and_restart_limited(assumption_level_start);
747 } else {
748 if let Some(var) = self.pick_branch_var() {
750 self.stats.decisions += 1;
751 self.trail.new_decision_level();
752
753 let polarity = if self.rand_bool(self.config.random_polarity_prob) {
754 self.rand_bool(0.5)
755 } else {
756 self.phase.get(var.index()).copied().unwrap_or(false)
757 };
758 let lit = if polarity {
759 Lit::pos(var)
760 } else {
761 Lit::neg(var)
762 };
763 self.trail.assign_decision(lit);
764 } else {
765 self.save_model();
767 self.backtrack(assumption_level_start);
768 return (SolverResult::Sat, None);
769 }
770 }
771 }
772 }
773
774 fn extract_assumption_core(&self, assumptions: &[Lit], conflict_idx: usize) -> Vec<Lit> {
776 let mut core = Vec::new();
778 let conflict_lit = assumptions[conflict_idx];
779
780 for &lit in &assumptions[..=conflict_idx] {
782 if self.seen.get(lit.var().index()).copied().unwrap_or(false) || lit == conflict_lit {
783 core.push(lit);
784 }
785 }
786
787 if core.is_empty() {
789 core.push(conflict_lit);
790 }
791
792 core
793 }
794
795 fn analyze_assumption_conflict(&mut self, assumptions: &[Lit]) -> Vec<Lit> {
797 let mut core = Vec::new();
799
800 for &lit in assumptions {
802 let var = lit.var();
803 if var.index() < self.trail.assignments().len() {
804 let value = self.trail.lit_value(lit);
805 if value.is_false() || self.seen.get(var.index()).copied().unwrap_or(false) {
807 core.push(lit);
808 }
809 }
810 }
811
812 if core.is_empty() {
814 core.extend(assumptions.iter().copied());
815 }
816
817 core
818 }
819
820 fn analyze_conflict_level(&self, conflict: ClauseId) -> u32 {
822 let clause = match self.clauses.get(conflict) {
823 Some(c) => c,
824 None => return 0,
825 };
826
827 let mut min_level = u32::MAX;
828 for lit in clause.lits.iter().copied() {
829 let level = self.trail.level(lit.var());
830 if level > 0 && level < min_level {
831 min_level = level;
832 }
833 }
834
835 if min_level == u32::MAX { 0 } else { min_level }
836 }
837
838 fn handle_clause_deletion_and_restart_limited(&mut self, min_level: u32) {
840 self.conflicts_since_deletion += 1;
841
842 if self.conflicts_since_deletion >= self.config.clause_deletion_threshold as u64 {
843 self.reduce_clause_database();
844 self.conflicts_since_deletion = 0;
845 }
846
847 if self.stats.conflicts >= self.restart_threshold {
848 self.backtrack(min_level);
850 self.stats.restarts += 1;
851 self.luby_index += 1;
852 self.restart_threshold =
853 self.stats.conflicts + self.config.restart_interval * Self::luby(self.luby_index);
854 }
855 }
856
857 fn propagate(&mut self) -> Option<ClauseId> {
859 while let Some(lit) = self.trail.next_to_propagate() {
860 self.stats.propagations += 1;
861
862 let binary_implications = self.binary_graph.get(lit).to_vec();
864 for &(implied_lit, clause_id) in &binary_implications {
865 let value = self.trail.lit_value(implied_lit);
866 if value.is_false() {
867 return Some(clause_id);
869 } else if !value.is_defined() {
870 self.trail.assign_propagation(implied_lit, clause_id);
872
873 if self.config.enable_lazy_hyper_binary {
875 self.check_hyper_binary_resolution(lit, implied_lit, clause_id);
876 }
877 }
878 }
879
880 let watches = std::mem::take(self.watches.get_mut(lit));
882
883 let mut i = 0;
884 while i < watches.len() {
885 let watcher = watches[i];
886
887 if self.trail.lit_value(watcher.blocker).is_true() {
889 i += 1;
890 continue;
891 }
892
893 let clause = match self.clauses.get_mut(watcher.clause) {
894 Some(c) if !c.deleted => c,
895 _ => {
896 i += 1;
897 continue;
898 }
899 };
900
901 if clause.lits[0] == lit.negate() {
903 clause.swap(0, 1);
904 }
905
906 let first = clause.lits[0];
908 if self.trail.lit_value(first).is_true() {
909 self.watches
911 .get_mut(lit)
912 .push(Watcher::new(watcher.clause, first));
913 i += 1;
914 continue;
915 }
916
917 let mut found = false;
919 for j in 2..clause.lits.len() {
920 let l = clause.lits[j];
921 if !self.trail.lit_value(l).is_false() {
922 clause.swap(1, j);
923 self.watches
924 .add(clause.lits[1].negate(), Watcher::new(watcher.clause, first));
925 found = true;
926 break;
927 }
928 }
929
930 if found {
931 i += 1;
932 continue;
933 }
934
935 self.watches
937 .get_mut(lit)
938 .push(Watcher::new(watcher.clause, first));
939
940 if self.trail.lit_value(first).is_false() {
941 for j in (i + 1)..watches.len() {
944 self.watches.get_mut(lit).push(watches[j]);
945 }
946 return Some(watcher.clause);
947 } else {
948 self.trail.assign_propagation(first, watcher.clause);
950
951 if self.config.enable_lazy_hyper_binary {
953 self.check_hyper_binary_resolution(lit, first, watcher.clause);
954 }
955 }
956
957 i += 1;
958 }
959 }
960
961 None
962 }
963
964 fn analyze(&mut self, conflict: ClauseId) -> (u32, SmallVec<[Lit; 16]>) {
966 self.learnt.clear();
967 self.learnt.push(Lit::from_code(0)); let mut counter = 0;
970 let mut p = None;
971 let mut index = self.trail.assignments().len();
972 let current_level = self.trail.decision_level();
973
974 for s in &mut self.seen {
976 *s = false;
977 }
978
979 let mut reason_clause = conflict;
980
981 loop {
982 let Some(clause) = self.clauses.get(reason_clause) else {
984 break; };
986 let start = if p.is_some() { 1 } else { 0 };
987 let is_learned = clause.learned;
988
989 if is_learned {
991 if let Some(clause_mut) = self.clauses.get_mut(reason_clause) {
992 clause_mut.record_usage();
993 if clause_mut.lbd <= 2 {
995 clause_mut.promote_to_core();
996 }
997 clause_mut.activity += self.clause_bump_increment;
999 }
1000 }
1001
1002 let Some(clause) = self.clauses.get(reason_clause) else {
1003 break;
1004 };
1005 for &lit in &clause.lits[start..] {
1006 let var = lit.var();
1007 let level = self.trail.level(var);
1008
1009 if !self.seen[var.index()] && level > 0 {
1010 self.seen[var.index()] = true;
1011 self.vsids.bump(var);
1012 self.chb.bump(var);
1013 self.lrb.on_reason(var);
1014
1015 if level == current_level {
1016 counter += 1;
1017 } else {
1018 self.learnt.push(lit.negate());
1019 }
1020 }
1021 }
1022
1023 let mut current_lit;
1025 loop {
1026 index -= 1;
1027 current_lit = self.trail.assignments()[index];
1028 p = Some(current_lit);
1029 if self.seen[current_lit.var().index()] {
1030 break;
1031 }
1032 }
1033
1034 counter -= 1;
1035 if counter == 0 {
1036 break;
1037 }
1038
1039 let var = current_lit.var();
1040 match self.trail.reason(var) {
1041 Reason::Propagation(c) => reason_clause = c,
1042 _ => break,
1043 }
1044 }
1045
1046 if let Some(lit) = p {
1048 self.learnt[0] = lit.negate();
1049 }
1050
1051 self.minimize_learnt_clause();
1053
1054 let assertion_level = if self.learnt.len() == 1 {
1056 0
1057 } else {
1058 let mut max_level = 0;
1060 let mut max_idx = 1;
1061 for (i, &lit) in self.learnt.iter().enumerate().skip(1) {
1062 let level = self.trail.level(lit.var());
1063 if level > max_level {
1064 max_level = level;
1065 max_idx = i;
1066 }
1067 }
1068 self.learnt.swap(1, max_idx);
1070 max_level
1071 };
1072
1073 let backtrack_level = self.chrono_backtrack.compute_backtrack_level(
1075 &self.trail,
1076 &self.learnt,
1077 assertion_level,
1078 );
1079
1080 if backtrack_level != assertion_level {
1082 self.stats.chrono_backtracks += 1;
1083 } else {
1084 self.stats.non_chrono_backtracks += 1;
1085 }
1086
1087 (backtrack_level, self.learnt.clone())
1088 }
1089
1090 fn pick_branch_var(&mut self) -> Option<Var> {
1092 if self.config.use_lrb_branching {
1093 while let Some(var) = self.lrb.select() {
1095 if !self.trail.is_assigned(var) {
1096 self.lrb.on_assign(var);
1097 return Some(var);
1098 }
1099 }
1100 } else if self.config.use_chb_branching {
1101 if self.stats.decisions % 100 == 0 {
1104 self.chb.rebuild_heap();
1105 }
1106
1107 while let Some(var) = self.chb.pop_max() {
1108 if !self.trail.is_assigned(var) {
1109 return Some(var);
1110 }
1111 }
1112 } else {
1113 while let Some(var) = self.vsids.pop_max() {
1115 if !self.trail.is_assigned(var) {
1116 return Some(var);
1117 }
1118 }
1119 }
1120 None
1121 }
1122
1123 fn minimize_learnt_clause(&mut self) {
1134 if self.learnt.len() <= 2 {
1135 return;
1137 }
1138
1139 let original_len = self.learnt.len();
1140
1141 self.analyze_stack.clear();
1144
1145 let mut j = 1; for i in 1..self.learnt.len() {
1148 let lit = self.learnt[i];
1149 if self.lit_is_redundant(lit) {
1150 } else {
1152 self.learnt[j] = lit;
1154 j += 1;
1155 }
1156 }
1157 self.learnt.truncate(j);
1158
1159 self.strengthen_learnt_clause();
1163
1164 let final_len = self.learnt.len();
1166 if final_len < original_len {
1167 self.stats.minimizations += 1;
1168 self.stats.literals_removed += (original_len - final_len) as u64;
1169 }
1170 }
1171
1172 fn strengthen_learnt_clause(&mut self) {
1174 if self.learnt.len() <= 2 {
1175 return;
1176 }
1177
1178 let mut j = 1;
1180 for i in 1..self.learnt.len() {
1181 let lit = self.learnt[i];
1182 let var = lit.var();
1183
1184 if let Reason::Propagation(reason_id) = self.trail.reason(var) {
1186 if let Some(reason_clause) = self.clauses.get(reason_id) {
1187 if reason_clause.lits.len() == 2 {
1189 let other_lit = if reason_clause.lits[0] == lit.negate() {
1191 reason_clause.lits[1]
1192 } else if reason_clause.lits[1] == lit.negate() {
1193 reason_clause.lits[0]
1194 } else {
1195 self.learnt[j] = lit;
1197 j += 1;
1198 continue;
1199 };
1200
1201 if self.trail.level(other_lit.var()) == 0
1204 && self.seen[other_lit.var().index()]
1205 {
1206 continue;
1208 }
1209 }
1210 }
1211 }
1212
1213 self.learnt[j] = lit;
1215 j += 1;
1216 }
1217 self.learnt.truncate(j);
1218 }
1219
1220 fn lit_is_redundant(&mut self, lit: Lit) -> bool {
1227 let var = lit.var();
1228
1229 let reason = match self.trail.reason(var) {
1231 Reason::Decision => return false,
1232 Reason::Theory => return false, Reason::Propagation(c) => c,
1234 };
1235
1236 let reason_clause = match self.clauses.get(reason) {
1237 Some(c) => c,
1238 None => return false,
1239 };
1240
1241 for &reason_lit in &reason_clause.lits {
1243 if reason_lit == lit.negate() {
1244 continue;
1246 }
1247
1248 let reason_var = reason_lit.var();
1249
1250 if self.trail.level(reason_var) == 0 {
1252 continue;
1253 }
1254
1255 if self.seen[reason_var.index()] {
1257 continue;
1258 }
1259
1260 return false;
1263 }
1264
1265 true
1266 }
1267
1268 fn backtrack_with_phase_saving(&mut self, level: u32) {
1270 let phase = &mut self.phase;
1272 let lrb = &mut self.lrb;
1273 self.trail.backtrack_to_with_callback(level, |lit| {
1274 let var = lit.var();
1275 if var.index() < phase.len() {
1276 phase[var.index()] = lit.is_pos();
1277 }
1278 lrb.unassign(var);
1280 });
1281 }
1282
1283 fn backtrack(&mut self, level: u32) {
1285 self.trail.backtrack_to(level);
1286 }
1287
1288 fn luby(i: u64) -> u64 {
1292 let i = i + 1; let mut k = 1u32;
1296 while (1u64 << k) - 1 < i {
1297 k += 1;
1298 }
1299
1300 let seq_len = (1u64 << k) - 1;
1301
1302 if i == seq_len {
1303 1u64 << (k - 1)
1305 } else {
1306 let half_len = (1u64 << (k - 1)) - 1;
1309 if i <= half_len {
1310 Self::luby(i - 1) } else if i <= 2 * half_len {
1312 Self::luby(i - half_len - 1)
1313 } else {
1314 1u64 << (k - 1)
1315 }
1316 }
1317 }
1318
1319 fn restart(&mut self) {
1321 self.stats.restarts += 1;
1322 self.backtrack_with_phase_saving(0);
1323
1324 match self.config.restart_strategy {
1326 RestartStrategy::Luby => {
1327 self.luby_index += 1;
1328 self.restart_threshold = self.stats.conflicts
1329 + Self::luby(self.luby_index) * self.config.restart_interval;
1330 }
1331 RestartStrategy::Geometric => {
1332 let current_interval = if self.restart_threshold > self.stats.conflicts {
1333 self.restart_threshold - self.stats.conflicts
1334 } else {
1335 self.config.restart_interval
1336 };
1337 let next_interval =
1338 (current_interval as f64 * self.config.restart_multiplier) as u64;
1339 self.restart_threshold = self.stats.conflicts + next_interval;
1340 }
1341 RestartStrategy::Glucose => {
1342 let current_interval = if self.restart_threshold > self.stats.conflicts {
1346 self.restart_threshold - self.stats.conflicts
1347 } else {
1348 self.config.restart_interval
1349 };
1350
1351 let next_interval = if self.recent_lbd_count > 50 {
1353 let recent_avg = self.recent_lbd_sum / self.recent_lbd_count.max(1);
1354 if recent_avg < 5 {
1356 ((current_interval as f64) * 1.1) as u64
1358 } else {
1359 ((current_interval as f64) * 0.9) as u64
1361 }
1362 } else {
1363 current_interval
1364 };
1365
1366 self.restart_threshold = self.stats.conflicts + next_interval.max(100);
1367 }
1368 RestartStrategy::LocalLbd => {
1369 self.conflicts_since_local_restart += 1;
1372
1373 if self.conflicts_since_local_restart >= 50 && self.should_local_restart() {
1374 let local_level = self.compute_local_restart_level();
1376 self.backtrack_with_phase_saving(local_level);
1377 self.conflicts_since_local_restart = 0;
1378 self.recent_lbd_sum = 0;
1380 self.recent_lbd_count = 0;
1381 } else {
1382 let current_interval = if self.restart_threshold > self.stats.conflicts {
1384 self.restart_threshold - self.stats.conflicts
1385 } else {
1386 self.config.restart_interval
1387 };
1388 self.restart_threshold = self.stats.conflicts + current_interval;
1389 }
1390 return; }
1392 }
1393
1394 for i in 0..self.num_vars {
1396 let var = Var::new(i as u32);
1397 if !self.trail.is_assigned(var) && !self.vsids.contains(var) {
1398 self.vsids.insert(var);
1399 }
1400 }
1401 }
1402
1403 fn should_local_restart(&self) -> bool {
1406 if self.recent_lbd_count < 50 || self.global_lbd_count < 100 {
1407 return false;
1408 }
1409
1410 let recent_avg = self.recent_lbd_sum / self.recent_lbd_count.max(1);
1411 let global_avg = self.global_lbd_sum / self.global_lbd_count.max(1);
1412
1413 recent_avg * 4 > global_avg * 5
1415 }
1416
1417 fn compute_local_restart_level(&self) -> u32 {
1420 let current_level = self.trail.decision_level();
1421
1422 if current_level > 5 {
1424 current_level / 5
1425 } else {
1426 0
1427 }
1428 }
1429
1430 fn compute_lbd(&mut self, lits: &[Lit]) -> u32 {
1433 self.lbd_mark += 1;
1434 let mark = self.lbd_mark;
1435
1436 let mut count = 0u32;
1437 for &lit in lits {
1438 let level = self.trail.level(lit.var()) as usize;
1439 if level < self.level_marks.len() && self.level_marks[level] != mark {
1440 self.level_marks[level] = mark;
1441 count += 1;
1442 }
1443 }
1444
1445 count
1446 }
1447
1448 fn reduce_clause_database(&mut self) {
1453 use crate::clause::ClauseTier;
1454
1455 let mut core_candidates: Vec<(ClauseId, f64)> = Vec::new();
1456 let mut mid_candidates: Vec<(ClauseId, f64)> = Vec::new();
1457 let mut local_candidates: Vec<(ClauseId, f64)> = Vec::new();
1458
1459 for &cid in &self.learned_clause_ids {
1460 if let Some(clause) = self.clauses.get(cid) {
1461 if clause.deleted {
1462 continue;
1463 }
1464
1465 if clause.lits.len() <= 2 {
1467 continue;
1468 }
1469
1470 let is_reason = clause.lits.iter().any(|&lit| {
1473 let var = lit.var();
1474 if self.trail.is_assigned(var) {
1475 matches!(self.trail.reason(var), Reason::Propagation(r) if r == cid)
1476 } else {
1477 false
1478 }
1479 });
1480
1481 if !is_reason {
1482 match clause.tier {
1483 ClauseTier::Core => core_candidates.push((cid, clause.activity)),
1484 ClauseTier::Mid => mid_candidates.push((cid, clause.activity)),
1485 ClauseTier::Local => local_candidates.push((cid, clause.activity)),
1486 }
1487 }
1488 }
1489 }
1490
1491 core_candidates.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
1493 mid_candidates.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
1494 local_candidates.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
1495
1496 let num_core_delete = core_candidates.len() / 10;
1499 let num_mid_delete = (mid_candidates.len() * 3) / 10;
1501 let num_local_delete = (local_candidates.len() * 3) / 4;
1503
1504 for (cid, _) in core_candidates.iter().take(num_core_delete) {
1505 self.clauses.remove(*cid);
1506 self.stats.deleted_clauses += 1;
1507 }
1508
1509 for (cid, _) in mid_candidates.iter().take(num_mid_delete) {
1510 self.clauses.remove(*cid);
1511 self.stats.deleted_clauses += 1;
1512 }
1513
1514 for (cid, _) in local_candidates.iter().take(num_local_delete) {
1515 self.clauses.remove(*cid);
1516 self.stats.deleted_clauses += 1;
1517 }
1518
1519 self.learned_clause_ids
1521 .retain(|&cid| self.clauses.get(cid).is_some_and(|c| !c.deleted));
1522 }
1523
1524 fn save_model(&mut self) {
1526 self.model.resize(self.num_vars, LBool::Undef);
1527 for i in 0..self.num_vars {
1528 self.model[i] = self.trail.value(Var::new(i as u32));
1529 }
1530 }
1531
1532 #[must_use]
1534 pub fn model(&self) -> &[LBool] {
1535 &self.model
1536 }
1537
1538 #[must_use]
1540 pub fn model_value(&self, var: Var) -> LBool {
1541 self.model.get(var.index()).copied().unwrap_or(LBool::Undef)
1542 }
1543
1544 #[must_use]
1546 pub fn stats(&self) -> &SolverStats {
1547 &self.stats
1548 }
1549
1550 #[must_use]
1552 pub fn num_vars(&self) -> usize {
1553 self.num_vars
1554 }
1555
1556 #[must_use]
1558 pub fn num_clauses(&self) -> usize {
1559 self.clauses.len()
1560 }
1561
1562 pub fn push(&mut self) {
1568 self.trail.backtrack_to(0);
1571
1572 self.assertion_levels.push(self.clauses.num_original());
1573 self.assertion_trail_sizes.push(self.trail.size());
1574 self.assertion_clause_ids.push(Vec::new());
1575 }
1576
1577 pub fn pop(&mut self) {
1579 if self.assertion_levels.len() > 1 {
1580 self.assertion_levels.pop();
1581
1582 let trail_size = self.assertion_trail_sizes.pop().unwrap_or(0);
1584
1585 if let Some(clause_ids_to_remove) = self.assertion_clause_ids.pop() {
1587 for clause_id in clause_ids_to_remove {
1588 self.clauses.remove(clause_id);
1590
1591 self.learned_clause_ids.retain(|&id| id != clause_id);
1593
1594 }
1597 }
1598
1599 self.trail.backtrack_to_size(trail_size);
1602
1603 self.trail.backtrack_to(0);
1605
1606 self.trivially_unsat = false;
1608 }
1609 }
1610
1611 pub fn backtrack_to_root(&mut self) {
1616 self.trail.backtrack_to(0);
1617 }
1618
1619 pub fn reset(&mut self) {
1621 self.clauses = ClauseDatabase::new();
1622 self.trail.clear();
1623 self.watches.clear();
1624 self.vsids.clear();
1625 self.chb.clear();
1626 self.stats = SolverStats::default();
1627 self.learnt.clear();
1628 self.seen.clear();
1629 self.analyze_stack.clear();
1630 self.assertion_levels.clear();
1631 self.assertion_levels.push(0);
1632 self.assertion_trail_sizes.clear();
1633 self.assertion_trail_sizes.push(0);
1634 self.assertion_clause_ids.clear();
1635 self.assertion_clause_ids.push(Vec::new());
1636 self.model.clear();
1637 self.num_vars = 0;
1638 self.restart_threshold = self.config.restart_interval;
1639 self.trivially_unsat = false;
1640 self.phase.clear();
1641 self.luby_index = 0;
1642 self.level_marks.clear();
1643 self.lbd_mark = 0;
1644 self.learned_clause_ids.clear();
1645 self.conflicts_since_deletion = 0;
1646 self.rng_state = 0x853c_49e6_748f_ea9b;
1647 self.recent_lbd_sum = 0;
1648 self.recent_lbd_count = 0;
1649 self.binary_graph.clear();
1650 self.global_lbd_sum = 0;
1651 self.global_lbd_count = 0;
1652 self.conflicts_since_local_restart = 0;
1653 }
1654
1655 pub fn solve_with_theory<T: TheoryCallback>(&mut self, theory: &mut T) -> SolverResult {
1664 if self.trivially_unsat {
1665 return SolverResult::Unsat;
1666 }
1667
1668 if self.propagate().is_some() {
1670 return SolverResult::Unsat;
1671 }
1672
1673 loop {
1674 if let Some(conflict) = self.propagate() {
1676 self.stats.conflicts += 1;
1677
1678 if self.trail.decision_level() == 0 {
1679 return SolverResult::Unsat;
1680 }
1681
1682 let (backtrack_level, learnt_clause) = self.analyze(conflict);
1683 theory.on_backtrack(backtrack_level);
1684 self.backtrack_with_phase_saving(backtrack_level);
1685 self.learn_clause(learnt_clause);
1686
1687 self.vsids.decay();
1688 self.clauses.decay_activity(self.config.clause_decay);
1689 self.handle_clause_deletion_and_restart();
1690 continue;
1691 }
1692
1693 loop {
1695 let assignments = self.trail.assignments().to_vec();
1697 let mut theory_conflict = None;
1698 let mut theory_propagations = Vec::new();
1699
1700 for &lit in &assignments {
1702 match theory.on_assignment(lit) {
1703 TheoryCheckResult::Sat => {}
1704 TheoryCheckResult::Conflict(conflict_lits) => {
1705 theory_conflict = Some(conflict_lits);
1706 break;
1707 }
1708 TheoryCheckResult::Propagated(props) => {
1709 theory_propagations.extend(props);
1710 }
1711 }
1712 }
1713
1714 if let Some(conflict_lits) = theory_conflict {
1716 self.stats.conflicts += 1;
1717
1718 if self.trail.decision_level() == 0 {
1719 return SolverResult::Unsat;
1720 }
1721
1722 let (backtrack_level, learnt_clause) =
1723 self.analyze_theory_conflict(&conflict_lits);
1724 theory.on_backtrack(backtrack_level);
1725 self.backtrack_with_phase_saving(backtrack_level);
1726 self.learn_clause(learnt_clause);
1727
1728 self.vsids.decay();
1729 self.clauses.decay_activity(self.config.clause_decay);
1730 self.handle_clause_deletion_and_restart();
1731 continue;
1732 }
1733
1734 let mut made_propagation = false;
1736 for (lit, reason_lits) in theory_propagations {
1737 if !self.trail.is_assigned(lit.var()) {
1738 let clause_id = self.add_theory_reason_clause(&reason_lits, lit);
1740 self.trail.assign_propagation(lit, clause_id);
1741 made_propagation = true;
1742 }
1743 }
1744
1745 if made_propagation {
1746 if let Some(conflict) = self.propagate() {
1748 self.stats.conflicts += 1;
1749
1750 if self.trail.decision_level() == 0 {
1751 return SolverResult::Unsat;
1752 }
1753
1754 let (backtrack_level, learnt_clause) = self.analyze(conflict);
1755 theory.on_backtrack(backtrack_level);
1756 self.backtrack_with_phase_saving(backtrack_level);
1757 self.learn_clause(learnt_clause);
1758
1759 self.vsids.decay();
1760 self.clauses.decay_activity(self.config.clause_decay);
1761 self.handle_clause_deletion_and_restart();
1762 }
1763 continue;
1764 }
1765
1766 break;
1767 }
1768
1769 if let Some(var) = self.pick_branch_var() {
1771 self.stats.decisions += 1;
1772 self.trail.new_decision_level();
1773
1774 let polarity = if self.rand_bool(self.config.random_polarity_prob) {
1775 self.rand_bool(0.5)
1776 } else {
1777 self.phase[var.index()]
1778 };
1779 let lit = if polarity {
1780 Lit::pos(var)
1781 } else {
1782 Lit::neg(var)
1783 };
1784 self.trail.assign_decision(lit);
1785 } else {
1786 match theory.final_check() {
1788 TheoryCheckResult::Sat => {
1789 self.save_model();
1790 return SolverResult::Sat;
1791 }
1792 TheoryCheckResult::Conflict(conflict_lits) => {
1793 self.stats.conflicts += 1;
1794
1795 if self.trail.decision_level() == 0 {
1796 return SolverResult::Unsat;
1797 }
1798
1799 let (backtrack_level, learnt_clause) =
1800 self.analyze_theory_conflict(&conflict_lits);
1801 theory.on_backtrack(backtrack_level);
1802 self.backtrack_with_phase_saving(backtrack_level);
1803 self.learn_clause(learnt_clause);
1804
1805 self.vsids.decay();
1806 self.clauses.decay_activity(self.config.clause_decay);
1807 self.handle_clause_deletion_and_restart();
1808 }
1809 TheoryCheckResult::Propagated(props) => {
1810 for (lit, reason_lits) in props {
1812 if !self.trail.is_assigned(lit.var()) {
1813 let clause_id = self.add_theory_reason_clause(&reason_lits, lit);
1814 self.trail.assign_propagation(lit, clause_id);
1815 }
1816 }
1817 }
1818 }
1819 }
1820 }
1821 }
1822
1823 fn analyze_theory_conflict(&mut self, conflict_lits: &[Lit]) -> (u32, SmallVec<[Lit; 16]>) {
1825 self.learnt.clear();
1826 self.learnt.push(Lit::from_code(0)); let mut counter = 0;
1829 let current_level = self.trail.decision_level();
1830
1831 for s in &mut self.seen {
1833 *s = false;
1834 }
1835
1836 for &lit in conflict_lits {
1838 let var = lit.var();
1839 let level = self.trail.level(var);
1840
1841 if !self.seen[var.index()] && level > 0 {
1842 self.seen[var.index()] = true;
1843 self.vsids.bump(var);
1844 self.chb.bump(var);
1845
1846 if level == current_level {
1847 counter += 1;
1848 } else {
1849 self.learnt.push(lit.negate());
1850 }
1851 }
1852 }
1853
1854 let mut index = self.trail.assignments().len();
1856 let mut p = None;
1857
1858 while counter > 0 {
1859 index -= 1;
1860 let current_lit = self.trail.assignments()[index];
1861 p = Some(current_lit);
1862 let var = current_lit.var();
1863
1864 if self.seen[var.index()] {
1865 counter -= 1;
1866
1867 if counter > 0 {
1868 if let Reason::Propagation(reason_clause) = self.trail.reason(var) {
1870 if let Some(clause) = self.clauses.get(reason_clause) {
1871 for &lit in &clause.lits[1..] {
1872 let reason_var = lit.var();
1873 let level = self.trail.level(reason_var);
1874
1875 if !self.seen[reason_var.index()] && level > 0 {
1876 self.seen[reason_var.index()] = true;
1877 self.vsids.bump(reason_var);
1878 self.chb.bump(reason_var);
1879
1880 if level == current_level {
1881 counter += 1;
1882 } else {
1883 self.learnt.push(lit.negate());
1884 }
1885 }
1886 }
1887 }
1888 }
1889 }
1890 }
1891 }
1892
1893 if let Some(uip) = p {
1895 self.learnt[0] = uip.negate();
1896 }
1897
1898 self.minimize_learnt_clause();
1900
1901 let backtrack_level = if self.learnt.len() == 1 {
1903 0
1904 } else {
1905 let mut max_level = 0;
1906 let mut max_idx = 1;
1907 for (i, &lit) in self.learnt.iter().enumerate().skip(1) {
1908 let level = self.trail.level(lit.var());
1909 if level > max_level {
1910 max_level = level;
1911 max_idx = i;
1912 }
1913 }
1914 self.learnt.swap(1, max_idx);
1915 max_level
1916 };
1917
1918 (backtrack_level, self.learnt.clone())
1919 }
1920
1921 fn add_theory_reason_clause(&mut self, reason_lits: &[Lit], propagated_lit: Lit) -> ClauseId {
1924 let mut clause_lits: SmallVec<[Lit; 8]> = SmallVec::new();
1925 clause_lits.push(propagated_lit);
1926 for &lit in reason_lits {
1927 clause_lits.push(lit.negate());
1928 }
1929
1930 let clause_id = self.clauses.add_learned(clause_lits.iter().copied());
1931
1932 if clause_lits.len() >= 2 {
1934 let lit0 = clause_lits[0];
1935 let lit1 = clause_lits[1];
1936 self.watches
1937 .add(lit0.negate(), Watcher::new(clause_id, lit1));
1938 self.watches
1939 .add(lit1.negate(), Watcher::new(clause_id, lit0));
1940 }
1941
1942 clause_id
1943 }
1944
1945 fn learn_clause(&mut self, learnt_clause: SmallVec<[Lit; 16]>) {
1948 if learnt_clause.len() == 1 {
1949 self.trail.assign_decision(learnt_clause[0]);
1950 self.stats.unit_clauses += 1;
1951 self.stats.learned_clauses += 1;
1952 } else if learnt_clause.len() == 2 {
1953 let lbd = self.compute_lbd(&learnt_clause);
1955 let clause_id = self.clauses.add_learned(learnt_clause.iter().copied());
1956 self.stats.learned_clauses += 1;
1957 self.stats.binary_clauses += 1;
1958 self.stats.total_lbd += lbd as u64;
1959
1960 if let Some(clause) = self.clauses.get_mut(clause_id) {
1961 clause.lbd = lbd;
1962 }
1963
1964 self.learned_clause_ids.push(clause_id);
1965
1966 let lit0 = learnt_clause[0];
1967 let lit1 = learnt_clause[1];
1968
1969 self.binary_graph.add(lit0.negate(), lit1, clause_id);
1971 self.binary_graph.add(lit1.negate(), lit0, clause_id);
1972
1973 self.watches
1974 .add(lit0.negate(), Watcher::new(clause_id, lit1));
1975 self.watches
1976 .add(lit1.negate(), Watcher::new(clause_id, lit0));
1977
1978 self.trail.assign_propagation(learnt_clause[0], clause_id);
1979 } else {
1980 let lbd = self.compute_lbd(&learnt_clause);
1981 self.stats.total_lbd += lbd as u64;
1982 let clause_id = self.clauses.add_learned(learnt_clause.iter().copied());
1983 self.stats.learned_clauses += 1;
1984
1985 if let Some(clause) = self.clauses.get_mut(clause_id) {
1986 clause.lbd = lbd;
1987 }
1988
1989 self.learned_clause_ids.push(clause_id);
1990
1991 let lit0 = learnt_clause[0];
1992 let lit1 = learnt_clause[1];
1993 self.watches
1994 .add(lit0.negate(), Watcher::new(clause_id, lit1));
1995 self.watches
1996 .add(lit1.negate(), Watcher::new(clause_id, lit0));
1997
1998 self.trail.assign_propagation(learnt_clause[0], clause_id);
1999
2000 if learnt_clause.len() <= 5 && lbd <= 3 {
2002 self.check_subsumption(clause_id);
2003 }
2004 }
2005 }
2006
2007 fn check_subsumption(&mut self, new_clause_id: ClauseId) {
2010 let new_clause = match self.clauses.get(new_clause_id) {
2011 Some(c) => c.lits.clone(),
2012 None => return,
2013 };
2014
2015 if new_clause.len() > 10 {
2016 return; }
2018
2019 let mut to_remove = Vec::new();
2021 for &cid in &self.learned_clause_ids {
2022 if cid == new_clause_id {
2023 continue;
2024 }
2025
2026 if let Some(clause) = self.clauses.get(cid) {
2027 if clause.deleted || clause.lits.len() < new_clause.len() {
2028 continue;
2029 }
2030
2031 if new_clause.iter().all(|&lit| clause.lits.contains(&lit)) {
2033 to_remove.push(cid);
2034 }
2035 }
2036 }
2037
2038 for cid in to_remove {
2040 self.clauses.remove(cid);
2041 self.stats.deleted_clauses += 1;
2042 }
2043 }
2044
2045 fn handle_clause_deletion_and_restart(&mut self) {
2047 self.conflicts_since_deletion += 1;
2048
2049 if self.conflicts_since_deletion >= self.config.clause_deletion_threshold as u64 {
2050 self.reduce_clause_database();
2051 self.conflicts_since_deletion = 0;
2052 }
2053
2054 if self.stats.conflicts >= self.restart_threshold {
2055 self.restart();
2056 }
2057 }
2058
2059 #[must_use]
2061 pub fn trail(&self) -> &Trail {
2062 &self.trail
2063 }
2064
2065 #[must_use]
2067 pub fn decision_level(&self) -> u32 {
2068 self.trail.decision_level()
2069 }
2070
2071 fn rand_u64(&mut self) -> u64 {
2073 let mut x = self.rng_state;
2074 x ^= x << 13;
2075 x ^= x >> 7;
2076 x ^= x << 17;
2077 self.rng_state = x;
2078 x
2079 }
2080
2081 fn rand_f64(&mut self) -> f64 {
2083 const MAX: f64 = u64::MAX as f64;
2084 (self.rand_u64() as f64) / MAX
2085 }
2086
2087 fn rand_bool(&mut self, probability: f64) -> bool {
2089 self.rand_f64() < probability
2090 }
2091
2092 fn check_hyper_binary_resolution(&mut self, _lit: Lit, implied: Lit, reason_id: ClauseId) {
2096 if self.trail.decision_level() < 2 {
2098 return;
2099 }
2100
2101 let reason_clause = match self.clauses.get(reason_id) {
2103 Some(c) if c.lits.len() >= 2 && c.lits.len() <= 4 => c.lits.clone(),
2104 _ => return,
2105 };
2106
2107 let current_level = self.trail.decision_level();
2110 let mut current_level_lits = SmallVec::<[Lit; 4]>::new();
2111
2112 for &reason_lit in &reason_clause {
2113 if reason_lit != implied {
2114 let var = reason_lit.var();
2115 if self.trail.level(var) == current_level {
2116 current_level_lits.push(reason_lit);
2117 }
2118 }
2119 }
2120
2121 if current_level_lits.len() == 1 {
2124 let other_lit = current_level_lits[0];
2125
2126 let binary_clause_lits = [other_lit.negate(), implied];
2129
2130 if !self.has_binary_implication(other_lit, implied) {
2132 let clause_id = self.clauses.add_learned(binary_clause_lits.iter().copied());
2134 self.binary_graph.add(other_lit, implied, clause_id);
2135 self.binary_graph
2136 .add(implied.negate(), other_lit.negate(), clause_id);
2137 self.stats.learned_clauses += 1;
2138 }
2139 }
2140 }
2141
2142 fn has_binary_implication(&self, from_lit: Lit, to_lit: Lit) -> bool {
2144 self.binary_graph
2145 .get(from_lit)
2146 .iter()
2147 .any(|(lit, _)| *lit == to_lit)
2148 }
2149
2150 fn vivify_clauses(&mut self) {
2153 if self.trail.decision_level() != 0 {
2154 return; }
2156
2157 let mut vivified_count = 0;
2158 let max_vivifications = 100; let clause_ids: Vec<ClauseId> = self
2162 .learned_clause_ids
2163 .iter()
2164 .copied()
2165 .take(max_vivifications)
2166 .collect();
2167
2168 for clause_id in clause_ids {
2169 if vivified_count >= max_vivifications {
2170 break;
2171 }
2172
2173 let clause_lits = match self.clauses.get(clause_id) {
2174 Some(c) if !c.deleted && c.lits.len() > 2 => c.lits.clone(),
2175 _ => continue,
2176 };
2177
2178 for skip_idx in 0..clause_lits.len() {
2181 let saved_level = self.trail.decision_level();
2183
2184 self.trail.new_decision_level();
2186 let mut conflict = false;
2187
2188 for (i, &lit) in clause_lits.iter().enumerate() {
2189 if i == skip_idx {
2190 continue;
2191 }
2192
2193 let value = self.trail.lit_value(lit);
2194 if value.is_true() {
2195 conflict = false;
2197 break;
2198 } else if value.is_false() {
2199 continue;
2201 } else {
2202 self.trail.assign_decision(lit.negate());
2204
2205 if self.propagate().is_some() {
2207 conflict = true;
2208 break;
2209 }
2210 }
2211 }
2212
2213 self.backtrack(saved_level);
2215
2216 if conflict {
2217 if let Some(clause) = self.clauses.get_mut(clause_id) {
2220 if clause.lits.len() > 2 {
2221 clause.lits.remove(skip_idx);
2222 vivified_count += 1;
2223 break; }
2225 }
2226 }
2227 }
2228 }
2229 }
2230
2231 fn inprocess(&mut self) {
2233 use crate::preprocessing::Preprocessor;
2234
2235 if self.trail.decision_level() != 0 {
2237 return;
2238 }
2239
2240 let mut preprocessor = Preprocessor::new(self.num_vars);
2242
2243 let _pure_elim = preprocessor.pure_literal_elimination(&mut self.clauses);
2245 let _subsumption = preprocessor.subsumption_elimination(&mut self.clauses);
2246
2247 self.strengthen_clauses_inprocessing();
2249
2250 }
2254
2255 fn strengthen_clauses_inprocessing(&mut self) {
2260 if self.trail.decision_level() != 0 {
2261 return;
2262 }
2263
2264 let max_clauses_to_strengthen = 50; let mut strengthened_count = 0;
2266
2267 let mut candidates: Vec<(ClauseId, u32)> = Vec::new();
2269
2270 for &clause_id in &self.learned_clause_ids {
2271 if let Some(clause) = self.clauses.get(clause_id) {
2272 if !clause.deleted && clause.lits.len() > 3 && clause.lbd > 2 {
2273 candidates.push((clause_id, clause.lbd));
2274 }
2275 }
2276 }
2277
2278 candidates.sort_by_key(|(_, lbd)| std::cmp::Reverse(*lbd));
2280
2281 for (clause_id, _) in candidates.iter().take(max_clauses_to_strengthen) {
2282 if strengthened_count >= max_clauses_to_strengthen {
2283 break;
2284 }
2285
2286 let clause_lits = match self.clauses.get(*clause_id) {
2287 Some(c) if !c.deleted && c.lits.len() > 3 => c.lits.clone(),
2288 _ => continue,
2289 };
2290
2291 let mut literals_to_remove = Vec::new();
2293
2294 for (i, &lit) in clause_lits.iter().enumerate() {
2295 let saved_level = self.trail.decision_level();
2297
2298 self.trail.new_decision_level();
2300 self.trail.assign_decision(lit.negate());
2301
2302 let conflict = self.propagate();
2304
2305 self.backtrack(saved_level);
2307
2308 if conflict.is_some() {
2309 let mut remaining: Vec<Lit> = clause_lits
2316 .iter()
2317 .enumerate()
2318 .filter(|(j, _)| *j != i)
2319 .map(|(_, &l)| l)
2320 .collect();
2321
2322 if remaining.len() >= 2 {
2324 remaining.sort_by_key(|l| l.code());
2326 let mut is_tautology = false;
2327 for k in 0..remaining.len() - 1 {
2328 if remaining[k] == remaining[k + 1].negate() {
2329 is_tautology = true;
2330 break;
2331 }
2332 }
2333
2334 if !is_tautology {
2335 literals_to_remove.push(i);
2336 break; }
2338 }
2339 }
2340 }
2341
2342 if !literals_to_remove.is_empty() {
2344 if let Some(clause) = self.clauses.get_mut(*clause_id) {
2346 for &idx in literals_to_remove.iter().rev() {
2348 if idx < clause.lits.len() {
2349 clause.lits.remove(idx);
2350 }
2351 }
2352 }
2353
2354 if let Some(clause) = self.clauses.get(*clause_id) {
2356 let lits_clone = clause.lits.clone();
2357 let new_lbd = self.compute_lbd(&lits_clone);
2358
2359 if let Some(clause) = self.clauses.get_mut(*clause_id) {
2361 clause.lbd = new_lbd;
2362 }
2363
2364 strengthened_count += 1;
2365 }
2366 }
2367 }
2368 }
2369}
2370
2371#[cfg(test)]
2372mod tests {
2373 use super::*;
2374
2375 #[test]
2376 fn test_empty_sat() {
2377 let mut solver = Solver::new();
2378 assert_eq!(solver.solve(), SolverResult::Sat);
2379 }
2380
2381 #[test]
2382 fn test_simple_sat() {
2383 let mut solver = Solver::new();
2384 let _x = solver.new_var();
2385 let _y = solver.new_var();
2386
2387 solver.add_clause_dimacs(&[1, 2]);
2389 solver.add_clause_dimacs(&[-1, 2]);
2391
2392 assert_eq!(solver.solve(), SolverResult::Sat);
2393 assert!(solver.model_value(Var::new(1)).is_true()); }
2395
2396 #[test]
2397 fn test_simple_unsat() {
2398 let mut solver = Solver::new();
2399 let _x = solver.new_var();
2400
2401 solver.add_clause_dimacs(&[1]);
2403 solver.add_clause_dimacs(&[-1]);
2405
2406 assert_eq!(solver.solve(), SolverResult::Unsat);
2407 }
2408
2409 #[test]
2410 fn test_pigeonhole_2_1() {
2411 let mut solver = Solver::new();
2413 let _p1h1 = solver.new_var(); let _p2h1 = solver.new_var(); solver.add_clause_dimacs(&[1]); solver.add_clause_dimacs(&[2]); solver.add_clause_dimacs(&[-1, -2]); assert_eq!(solver.solve(), SolverResult::Unsat);
2424 }
2425
2426 #[test]
2427 fn test_3sat_random() {
2428 let mut solver = Solver::new();
2429 for _ in 0..10 {
2430 solver.new_var();
2431 }
2432
2433 solver.add_clause_dimacs(&[1, 2, 3]);
2435 solver.add_clause_dimacs(&[-1, 4, 5]);
2436 solver.add_clause_dimacs(&[2, -3, 6]);
2437 solver.add_clause_dimacs(&[-4, 7, 8]);
2438 solver.add_clause_dimacs(&[5, -6, 9]);
2439 solver.add_clause_dimacs(&[-7, 8, 10]);
2440 solver.add_clause_dimacs(&[1, -8, -9]);
2441 solver.add_clause_dimacs(&[-2, 3, -10]);
2442
2443 let result = solver.solve();
2444 assert_eq!(result, SolverResult::Sat);
2445 }
2446
2447 #[test]
2448 fn test_luby_sequence() {
2449 assert_eq!(Solver::luby(0), 1);
2451 assert_eq!(Solver::luby(1), 1);
2452 assert_eq!(Solver::luby(2), 2);
2453 assert_eq!(Solver::luby(3), 1);
2454 assert_eq!(Solver::luby(4), 1);
2455 assert_eq!(Solver::luby(5), 2);
2456 assert_eq!(Solver::luby(6), 4);
2457 assert_eq!(Solver::luby(7), 1);
2458 }
2459
2460 #[test]
2461 fn test_phase_saving() {
2462 let mut solver = Solver::new();
2463 for _ in 0..5 {
2464 solver.new_var();
2465 }
2466
2467 solver.add_clause_dimacs(&[1, 2]);
2469 solver.add_clause_dimacs(&[-1, 3]);
2470 solver.add_clause_dimacs(&[-2, 4]);
2471 solver.add_clause_dimacs(&[-3, -4, 5]);
2472 solver.add_clause_dimacs(&[-5, 1]);
2473
2474 let result = solver.solve();
2475 assert_eq!(result, SolverResult::Sat);
2476 }
2477
2478 #[test]
2479 fn test_lbd_computation() {
2480 let mut solver = Solver::with_config(SolverConfig {
2482 clause_deletion_threshold: 5, ..SolverConfig::default()
2484 });
2485
2486 for _ in 0..20 {
2487 solver.new_var();
2488 }
2489
2490 solver.add_clause_dimacs(&[1, 2]); solver.add_clause_dimacs(&[3, 4]); solver.add_clause_dimacs(&[5, 6]); solver.add_clause_dimacs(&[-1, -3]); solver.add_clause_dimacs(&[-1, -5]); solver.add_clause_dimacs(&[-3, -5]); solver.add_clause_dimacs(&[-2, -4]); solver.add_clause_dimacs(&[-2, -6]); solver.add_clause_dimacs(&[-4, -6]); let result = solver.solve();
2509 assert_eq!(result, SolverResult::Unsat);
2510 assert!(solver.stats().conflicts > 0);
2512 }
2513
2514 #[test]
2515 fn test_clause_activity_decay() {
2516 let mut solver = Solver::new();
2517 for _ in 0..10 {
2518 solver.new_var();
2519 }
2520
2521 solver.add_clause_dimacs(&[1, 2, 3]);
2523 solver.add_clause_dimacs(&[-1, 4, 5]);
2524 solver.add_clause_dimacs(&[-2, -3, 6]);
2525
2526 let result = solver.solve();
2528 assert_eq!(result, SolverResult::Sat);
2529 }
2530
2531 #[test]
2532 fn test_clause_minimization() {
2533 let mut solver = Solver::new();
2536
2537 for _ in 0..15 {
2538 solver.new_var();
2539 }
2540
2541 solver.add_clause_dimacs(&[1, 6, 11]); solver.add_clause_dimacs(&[2, 7, 12]); solver.add_clause_dimacs(&[3, 8, 13]); solver.add_clause_dimacs(&[4, 9, 14]); solver.add_clause_dimacs(&[5, 10, 15]); solver.add_clause_dimacs(&[-1, -6]); solver.add_clause_dimacs(&[-1, -11]); solver.add_clause_dimacs(&[-6, -11]); solver.add_clause_dimacs(&[-2, -7]);
2558 solver.add_clause_dimacs(&[-2, -12]);
2559 solver.add_clause_dimacs(&[-7, -12]);
2560
2561 solver.add_clause_dimacs(&[-3, -8]);
2562 solver.add_clause_dimacs(&[-3, -13]);
2563 solver.add_clause_dimacs(&[-8, -13]);
2564
2565 solver.add_clause_dimacs(&[-1, -2]); solver.add_clause_dimacs(&[-6, -7]); solver.add_clause_dimacs(&[-11, -12]); solver.add_clause_dimacs(&[-2, -3]); solver.add_clause_dimacs(&[-7, -8]);
2572 solver.add_clause_dimacs(&[-12, -13]);
2573
2574 let result = solver.solve();
2575 assert_eq!(result, SolverResult::Sat);
2576
2577 assert!(solver.stats().conflicts > 0 || solver.stats().learned_clauses > 0);
2579 }
2580
2581 struct NullTheory;
2583
2584 impl TheoryCallback for NullTheory {
2585 fn on_assignment(&mut self, _lit: Lit) -> TheoryCheckResult {
2586 TheoryCheckResult::Sat
2587 }
2588
2589 fn final_check(&mut self) -> TheoryCheckResult {
2590 TheoryCheckResult::Sat
2591 }
2592
2593 fn on_backtrack(&mut self, _level: u32) {}
2594 }
2595
2596 #[test]
2597 fn test_solve_with_theory_sat() {
2598 let mut solver = Solver::new();
2599 let mut theory = NullTheory;
2600
2601 let _x = solver.new_var();
2602 let _y = solver.new_var();
2603
2604 solver.add_clause_dimacs(&[1, 2]);
2606 solver.add_clause_dimacs(&[-1, 2]);
2608
2609 assert_eq!(solver.solve_with_theory(&mut theory), SolverResult::Sat);
2610 assert!(solver.model_value(Var::new(1)).is_true()); }
2612
2613 #[test]
2614 fn test_solve_with_theory_unsat() {
2615 let mut solver = Solver::new();
2616 let mut theory = NullTheory;
2617
2618 let _x = solver.new_var();
2619
2620 solver.add_clause_dimacs(&[1]);
2622 solver.add_clause_dimacs(&[-1]);
2624
2625 assert_eq!(solver.solve_with_theory(&mut theory), SolverResult::Unsat);
2626 }
2627
2628 struct ImplicationTheory {
2630 x0_true: bool,
2632 }
2633
2634 impl ImplicationTheory {
2635 fn new() -> Self {
2636 Self { x0_true: false }
2637 }
2638 }
2639
2640 impl TheoryCallback for ImplicationTheory {
2641 fn on_assignment(&mut self, lit: Lit) -> TheoryCheckResult {
2642 if lit.var().index() == 0 && lit.is_pos() {
2644 self.x0_true = true;
2645 let reason: SmallVec<[Lit; 8]> = smallvec::smallvec![Lit::pos(Var::new(0))];
2648 return TheoryCheckResult::Propagated(vec![(Lit::pos(Var::new(1)), reason)]);
2649 }
2650 TheoryCheckResult::Sat
2651 }
2652
2653 fn final_check(&mut self) -> TheoryCheckResult {
2654 TheoryCheckResult::Sat
2655 }
2656
2657 fn on_backtrack(&mut self, _level: u32) {
2658 self.x0_true = false;
2659 }
2660 }
2661
2662 #[test]
2663 fn test_theory_propagation() {
2664 let mut solver = Solver::new();
2665 let mut theory = ImplicationTheory::new();
2666
2667 let _x0 = solver.new_var();
2668 let _x1 = solver.new_var();
2669
2670 solver.add_clause_dimacs(&[1]);
2672
2673 let result = solver.solve_with_theory(&mut theory);
2674 assert_eq!(result, SolverResult::Sat);
2675
2676 assert!(solver.model_value(Var::new(0)).is_true());
2678 assert!(solver.model_value(Var::new(1)).is_true());
2680 }
2681
2682 struct MutexTheory {
2684 x0_true: Option<Lit>,
2685 x1_true: Option<Lit>,
2686 }
2687
2688 impl MutexTheory {
2689 fn new() -> Self {
2690 Self {
2691 x0_true: None,
2692 x1_true: None,
2693 }
2694 }
2695 }
2696
2697 impl TheoryCallback for MutexTheory {
2698 fn on_assignment(&mut self, lit: Lit) -> TheoryCheckResult {
2699 if lit.var().index() == 0 && lit.is_pos() {
2700 self.x0_true = Some(lit);
2701 }
2702 if lit.var().index() == 1 && lit.is_pos() {
2703 self.x1_true = Some(lit);
2704 }
2705
2706 if self.x0_true.is_some() && self.x1_true.is_some() {
2708 let conflict: SmallVec<[Lit; 8]> = smallvec::smallvec![
2710 Lit::pos(Var::new(0)), Lit::pos(Var::new(1)) ];
2713 return TheoryCheckResult::Conflict(conflict);
2714 }
2715 TheoryCheckResult::Sat
2716 }
2717
2718 fn final_check(&mut self) -> TheoryCheckResult {
2719 if self.x0_true.is_some() && self.x1_true.is_some() {
2720 let conflict: SmallVec<[Lit; 8]> =
2721 smallvec::smallvec![Lit::pos(Var::new(0)), Lit::pos(Var::new(1))];
2722 return TheoryCheckResult::Conflict(conflict);
2723 }
2724 TheoryCheckResult::Sat
2725 }
2726
2727 fn on_backtrack(&mut self, _level: u32) {
2728 self.x0_true = None;
2729 self.x1_true = None;
2730 }
2731 }
2732
2733 #[test]
2734 fn test_theory_conflict() {
2735 let mut solver = Solver::new();
2736 let mut theory = MutexTheory::new();
2737
2738 let _x0 = solver.new_var();
2739 let _x1 = solver.new_var();
2740
2741 solver.add_clause_dimacs(&[1]);
2743 solver.add_clause_dimacs(&[2]);
2744
2745 let result = solver.solve_with_theory(&mut theory);
2746 assert_eq!(result, SolverResult::Unsat);
2747 }
2748
2749 #[test]
2750 fn test_solve_with_assumptions_sat() {
2751 let mut solver = Solver::new();
2752
2753 let x0 = solver.new_var();
2754 let x1 = solver.new_var();
2755
2756 solver.add_clause([Lit::pos(x0), Lit::pos(x1)]);
2758
2759 let assumptions = [Lit::pos(x0)];
2761 let (result, core) = solver.solve_with_assumptions(&assumptions);
2762
2763 assert_eq!(result, SolverResult::Sat);
2764 assert!(core.is_none());
2765 }
2766
2767 #[test]
2768 fn test_solve_with_assumptions_unsat() {
2769 let mut solver = Solver::new();
2770
2771 let x0 = solver.new_var();
2772 let x1 = solver.new_var();
2773
2774 solver.add_clause([Lit::neg(x0), Lit::neg(x1)]);
2776
2777 let assumptions = [Lit::pos(x0), Lit::pos(x1)];
2779 let (result, core) = solver.solve_with_assumptions(&assumptions);
2780
2781 assert_eq!(result, SolverResult::Unsat);
2782 assert!(core.is_some());
2783 let core = core.unwrap();
2784 assert!(!core.is_empty());
2786 }
2787
2788 #[test]
2789 fn test_solve_with_assumptions_core_extraction() {
2790 let mut solver = Solver::new();
2791
2792 let x0 = solver.new_var();
2793 let x1 = solver.new_var();
2794 let x2 = solver.new_var();
2795
2796 solver.add_clause([Lit::neg(x0)]);
2798
2799 let assumptions = [Lit::pos(x0), Lit::pos(x1), Lit::pos(x2)];
2802 let (result, core) = solver.solve_with_assumptions(&assumptions);
2803
2804 assert_eq!(result, SolverResult::Unsat);
2805 assert!(core.is_some());
2806 let core = core.unwrap();
2807 assert!(core.contains(&Lit::pos(x0)));
2809 }
2810
2811 #[test]
2812 fn test_solve_with_assumptions_incremental() {
2813 let mut solver = Solver::new();
2814
2815 let x0 = solver.new_var();
2816 let x1 = solver.new_var();
2817
2818 solver.add_clause([Lit::pos(x0), Lit::pos(x1)]);
2820
2821 let (result1, _) = solver.solve_with_assumptions(&[Lit::neg(x0)]);
2823 assert_eq!(result1, SolverResult::Sat);
2824
2825 let (result2, core2) = solver.solve_with_assumptions(&[Lit::neg(x0), Lit::neg(x1)]);
2827 assert_eq!(result2, SolverResult::Unsat);
2828 assert!(core2.is_some());
2829
2830 let (result3, _) = solver.solve_with_assumptions(&[Lit::pos(x0)]);
2832 assert_eq!(result3, SolverResult::Sat);
2833 }
2834
2835 #[test]
2836 fn test_push_pop_simple() {
2837 let mut solver = Solver::new();
2838
2839 let x0 = solver.new_var();
2840
2841 assert_eq!(solver.solve(), SolverResult::Sat);
2843
2844 solver.push();
2846 solver.add_clause([Lit::pos(x0)]);
2847 assert_eq!(solver.solve(), SolverResult::Sat);
2848 assert!(solver.model_value(x0).is_true());
2849
2850 solver.pop();
2852 let result = solver.solve();
2853 assert_eq!(
2854 result,
2855 SolverResult::Sat,
2856 "After pop, expected SAT but got {:?}. trivially_unsat={}",
2857 result,
2858 solver.trivially_unsat
2859 );
2860 }
2861
2862 #[test]
2863 fn test_push_pop_incremental() {
2864 let mut solver = Solver::new();
2865
2866 let x0 = solver.new_var();
2867 let x1 = solver.new_var();
2868 let x2 = solver.new_var();
2869
2870 solver.add_clause([Lit::pos(x0), Lit::pos(x1)]);
2872 assert_eq!(solver.solve(), SolverResult::Sat);
2873
2874 solver.push();
2876 solver.add_clause([Lit::neg(x0)]);
2877 assert_eq!(solver.solve(), SolverResult::Sat);
2878 assert!(solver.model_value(x1).is_true());
2880
2881 solver.push();
2883 solver.add_clause([Lit::neg(x1)]);
2884 assert_eq!(solver.solve(), SolverResult::Unsat);
2885
2886 solver.pop();
2888 assert_eq!(solver.solve(), SolverResult::Sat);
2889 assert!(solver.model_value(x1).is_true());
2890
2891 solver.pop();
2893 assert_eq!(solver.solve(), SolverResult::Sat);
2894 solver.push();
2898 solver.add_clause([Lit::pos(x0)]);
2899 solver.add_clause([Lit::pos(x2)]);
2900 assert_eq!(solver.solve(), SolverResult::Sat);
2901 assert!(solver.model_value(x0).is_true());
2902 assert!(solver.model_value(x2).is_true());
2903
2904 solver.pop();
2906 assert_eq!(solver.solve(), SolverResult::Sat);
2907 }
2908
2909 #[test]
2910 fn test_push_pop_with_learned_clauses() {
2911 let mut solver = Solver::new();
2912
2913 let x0 = solver.new_var();
2914 let x1 = solver.new_var();
2915 let x2 = solver.new_var();
2916
2917 solver.add_clause([Lit::pos(x0), Lit::pos(x1)]);
2920 solver.add_clause([Lit::neg(x0), Lit::pos(x2)]);
2921 solver.add_clause([Lit::neg(x1), Lit::pos(x2)]);
2922
2923 assert_eq!(solver.solve(), SolverResult::Sat);
2924
2925 solver.push();
2927 solver.add_clause([Lit::neg(x2)]);
2928
2929 assert_eq!(solver.solve(), SolverResult::Unsat);
2931
2932 solver.pop();
2934
2935 assert_eq!(solver.solve(), SolverResult::Sat);
2937 }
2938}