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_new_level(&mut self, _level: u32) {}
71
72 fn on_backtrack(&mut self, level: u32);
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum SolverResult {
79 Sat,
81 Unsat,
83 Unknown,
85}
86
87#[derive(Debug, Clone)]
89pub struct SolverConfig {
90 pub restart_interval: u64,
92 pub restart_multiplier: f64,
94 pub clause_deletion_threshold: usize,
96 pub var_decay: f64,
98 pub clause_decay: f64,
100 pub random_polarity_prob: f64,
102 pub restart_strategy: RestartStrategy,
104 pub enable_lazy_hyper_binary: bool,
106 pub use_chb_branching: bool,
108 pub use_lrb_branching: bool,
110 pub enable_inprocessing: bool,
112 pub inprocessing_interval: u64,
114 pub enable_chronological_backtrack: bool,
116 pub chrono_backtrack_threshold: u32,
118}
119
120#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub enum RestartStrategy {
123 Luby,
125 Geometric,
127 Glucose,
129 LocalLbd,
131}
132
133impl Default for SolverConfig {
134 fn default() -> Self {
135 Self {
136 restart_interval: 100,
137 restart_multiplier: 1.5,
138 clause_deletion_threshold: 10000,
139 var_decay: 0.95,
140 clause_decay: 0.999,
141 random_polarity_prob: 0.02,
142 restart_strategy: RestartStrategy::Luby,
143 enable_lazy_hyper_binary: true,
144 use_chb_branching: false,
145 use_lrb_branching: false,
146 enable_inprocessing: false,
147 inprocessing_interval: 5000,
148 enable_chronological_backtrack: true,
149 chrono_backtrack_threshold: 100,
150 }
151 }
152}
153
154#[derive(Debug, Default, Clone)]
156pub struct SolverStats {
157 pub decisions: u64,
159 pub propagations: u64,
161 pub conflicts: u64,
163 pub restarts: u64,
165 pub learned_clauses: u64,
167 pub deleted_clauses: u64,
169 pub binary_clauses: u64,
171 pub unit_clauses: u64,
173 pub total_lbd: u64,
175 pub minimizations: u64,
177 pub literals_removed: u64,
179 pub chrono_backtracks: u64,
181 pub non_chrono_backtracks: u64,
183}
184
185impl SolverStats {
186 #[must_use]
188 pub fn avg_lbd(&self) -> f64 {
189 if self.learned_clauses == 0 {
190 0.0
191 } else {
192 self.total_lbd as f64 / self.learned_clauses as f64
193 }
194 }
195
196 #[must_use]
198 pub fn avg_decisions_per_conflict(&self) -> f64 {
199 if self.conflicts == 0 {
200 0.0
201 } else {
202 self.decisions as f64 / self.conflicts as f64
203 }
204 }
205
206 #[must_use]
208 pub fn propagations_per_conflict(&self) -> f64 {
209 if self.conflicts == 0 {
210 0.0
211 } else {
212 self.propagations as f64 / self.conflicts as f64
213 }
214 }
215
216 #[must_use]
218 pub fn deletion_ratio(&self) -> f64 {
219 if self.learned_clauses == 0 {
220 0.0
221 } else {
222 self.deleted_clauses as f64 / self.learned_clauses as f64
223 }
224 }
225
226 #[must_use]
228 pub fn chrono_backtrack_ratio(&self) -> f64 {
229 let total = self.chrono_backtracks + self.non_chrono_backtracks;
230 if total == 0 {
231 0.0
232 } else {
233 self.chrono_backtracks as f64 / total as f64
234 }
235 }
236
237 pub fn display(&self) {
239 println!("========== Solver Statistics ==========");
240 println!("Decisions: {:>12}", self.decisions);
241 println!("Propagations: {:>12}", self.propagations);
242 println!("Conflicts: {:>12}", self.conflicts);
243 println!("Restarts: {:>12}", self.restarts);
244 println!("Learned clauses: {:>12}", self.learned_clauses);
245 println!(" - Unit clauses: {:>12}", self.unit_clauses);
246 println!(" - Binary clauses: {:>12}", self.binary_clauses);
247 println!("Deleted clauses: {:>12}", self.deleted_clauses);
248 println!("Minimizations: {:>12}", self.minimizations);
249 println!("Literals removed: {:>12}", self.literals_removed);
250 println!("Chrono backtracks: {:>12}", self.chrono_backtracks);
251 println!("Non-chrono backtracks: {:>12}", self.non_chrono_backtracks);
252 println!("---------------------------------------");
253 println!("Avg LBD: {:>12.2}", self.avg_lbd());
254 println!(
255 "Avg decisions/conflict: {:>12.2}",
256 self.avg_decisions_per_conflict()
257 );
258 println!(
259 "Propagations/conflict: {:>12.2}",
260 self.propagations_per_conflict()
261 );
262 println!(
263 "Deletion ratio: {:>12.2}%",
264 self.deletion_ratio() * 100.0
265 );
266 println!(
267 "Chrono backtrack ratio: {:>12.2}%",
268 self.chrono_backtrack_ratio() * 100.0
269 );
270 println!("=======================================");
271 }
272}
273
274#[derive(Debug)]
276pub struct Solver {
277 config: SolverConfig,
279 num_vars: usize,
281 clauses: ClauseDatabase,
283 trail: Trail,
285 watches: WatchLists,
287 vsids: VSIDS,
289 chb: CHB,
291 lrb: LRB,
293 stats: SolverStats,
295 learnt: SmallVec<[Lit; 16]>,
297 seen: Vec<bool>,
299 analyze_stack: Vec<Lit>,
301 restart_threshold: u64,
303 assertion_levels: Vec<usize>,
305 assertion_trail_sizes: Vec<usize>,
307 assertion_clause_ids: Vec<Vec<ClauseId>>,
309 model: Vec<LBool>,
311 trivially_unsat: bool,
313 phase: Vec<bool>,
315 luby_index: u64,
317 level_marks: Vec<u32>,
319 lbd_mark: u32,
321 learned_clause_ids: Vec<ClauseId>,
323 conflicts_since_deletion: u64,
325 rng_state: u64,
327 recent_lbd_sum: u64,
329 recent_lbd_count: u64,
331 binary_graph: BinaryImplicationGraph,
333 global_lbd_sum: u64,
335 global_lbd_count: u64,
337 conflicts_since_local_restart: u64,
339 conflicts_since_inprocessing: u64,
341 chrono_backtrack: ChronoBacktrack,
343 clause_bump_increment: f64,
345}
346
347impl Default for Solver {
348 fn default() -> Self {
349 Self::new()
350 }
351}
352
353impl Solver {
354 #[must_use]
356 pub fn new() -> Self {
357 Self::with_config(SolverConfig::default())
358 }
359
360 #[must_use]
362 pub fn with_config(config: SolverConfig) -> Self {
363 let chrono_enabled = config.enable_chronological_backtrack;
364 let chrono_threshold = config.chrono_backtrack_threshold;
365
366 Self {
367 restart_threshold: config.restart_interval,
368 config,
369 num_vars: 0,
370 clauses: ClauseDatabase::new(),
371 trail: Trail::new(0),
372 watches: WatchLists::new(0),
373 vsids: VSIDS::new(0),
374 chb: CHB::new(0),
375 lrb: LRB::new(0),
376 stats: SolverStats::default(),
377 learnt: SmallVec::new(),
378 seen: Vec::new(),
379 analyze_stack: Vec::new(),
380 assertion_levels: vec![0],
381 assertion_trail_sizes: vec![0],
382 assertion_clause_ids: vec![Vec::new()],
383 model: Vec::new(),
384 trivially_unsat: false,
385 phase: Vec::new(),
386 luby_index: 0,
387 level_marks: Vec::new(),
388 lbd_mark: 0,
389 learned_clause_ids: Vec::new(),
390 conflicts_since_deletion: 0,
391 rng_state: 0x853c_49e6_748f_ea9b, recent_lbd_sum: 0,
393 recent_lbd_count: 0,
394 binary_graph: BinaryImplicationGraph::new(0),
395 global_lbd_sum: 0,
396 global_lbd_count: 0,
397 conflicts_since_local_restart: 0,
398 conflicts_since_inprocessing: 0,
399 chrono_backtrack: ChronoBacktrack::new(chrono_enabled, chrono_threshold),
400 clause_bump_increment: 1.0,
401 }
402 }
403
404 pub fn new_var(&mut self) -> Var {
406 let var = Var::new(self.num_vars as u32);
407 self.num_vars += 1;
408 self.trail.resize(self.num_vars);
409 self.watches.resize(self.num_vars);
410 self.binary_graph.resize(self.num_vars);
411 self.vsids.insert(var);
412 self.chb.insert(var);
413 self.lrb.resize(self.num_vars);
414 self.seen.resize(self.num_vars, false);
415 self.model.resize(self.num_vars, LBool::Undef);
416 self.phase.resize(self.num_vars, false); if self.level_marks.len() < self.num_vars {
419 self.level_marks.resize(self.num_vars, 0);
420 }
421 var
422 }
423
424 pub fn ensure_vars(&mut self, n: usize) {
426 while self.num_vars < n {
427 self.new_var();
428 }
429 }
430
431 pub fn add_clause(&mut self, lits: impl IntoIterator<Item = Lit>) -> bool {
433 let mut clause_lits: SmallVec<[Lit; 8]> = lits.into_iter().collect();
434
435 for lit in &clause_lits {
437 let var_idx = lit.var().index();
438 if var_idx >= self.num_vars {
439 self.ensure_vars(var_idx + 1);
440 }
441 }
442
443 clause_lits.sort_by_key(|l| l.code());
445 clause_lits.dedup();
446
447 for i in 0..clause_lits.len() {
449 for j in (i + 1)..clause_lits.len() {
450 if clause_lits[i] == clause_lits[j].negate() {
451 return true; }
453 }
454 }
455
456 match clause_lits.len() {
458 0 => {
459 self.trivially_unsat = true;
460 return false; }
462 1 => {
463 let lit = clause_lits[0];
465 if self.trail.lit_value(lit).is_false() {
466 self.trivially_unsat = true;
467 return false;
468 }
469 if !self.trail.is_assigned(lit.var()) {
470 self.trail.assign_decision(lit);
471 }
472 return true;
473 }
474 2 => {
475 let clause_id = self.clauses.add_original(clause_lits.iter().copied());
477
478 if let Some(current_level_clauses) = self.assertion_clause_ids.last_mut() {
480 current_level_clauses.push(clause_id);
481 }
482
483 let lit0 = clause_lits[0];
484 let lit1 = clause_lits[1];
485
486 self.binary_graph.add(lit0.negate(), lit1, clause_id);
488 self.binary_graph.add(lit1.negate(), lit0, clause_id);
489
490 self.watches
492 .add(lit0.negate(), Watcher::new(clause_id, lit1));
493 self.watches
494 .add(lit1.negate(), Watcher::new(clause_id, lit0));
495
496 return true;
497 }
498 _ => {}
499 }
500
501 let clause_id = self.clauses.add_original(clause_lits.iter().copied());
503
504 if let Some(current_level_clauses) = self.assertion_clause_ids.last_mut() {
506 current_level_clauses.push(clause_id);
507 }
508
509 let lit0 = clause_lits[0];
511 let lit1 = clause_lits[1];
512
513 self.watches
514 .add(lit0.negate(), Watcher::new(clause_id, lit1));
515 self.watches
516 .add(lit1.negate(), Watcher::new(clause_id, lit0));
517
518 true
519 }
520
521 pub fn add_clause_dimacs(&mut self, lits: &[i32]) -> bool {
523 self.add_clause(lits.iter().map(|&l| Lit::from_dimacs(l)))
524 }
525
526 pub fn solve(&mut self) -> SolverResult {
528 if self.trivially_unsat {
530 return SolverResult::Unsat;
531 }
532
533 if self.propagate().is_some() {
535 return SolverResult::Unsat;
536 }
537
538 loop {
539 if let Some(conflict) = self.propagate() {
541 self.stats.conflicts += 1;
542 self.conflicts_since_inprocessing += 1;
543
544 if self.trail.decision_level() == 0 {
545 return SolverResult::Unsat;
546 }
547
548 let (backtrack_level, learnt_clause) = self.analyze(conflict);
550
551 self.backtrack_with_phase_saving(backtrack_level);
553
554 if learnt_clause.len() == 1 {
556 self.trail.assign_decision(learnt_clause[0]);
557 } else {
558 let lbd = self.compute_lbd(&learnt_clause);
560
561 self.recent_lbd_sum += u64::from(lbd);
563 self.recent_lbd_count += 1;
564 self.global_lbd_sum += u64::from(lbd);
565 self.global_lbd_count += 1;
566
567 if self.recent_lbd_count >= 5000 {
569 self.recent_lbd_sum /= 2;
570 self.recent_lbd_count /= 2;
571 }
572
573 let clause_id = self.clauses.add_learned(learnt_clause.iter().copied());
574 self.stats.learned_clauses += 1;
575
576 if let Some(clause) = self.clauses.get_mut(clause_id) {
578 clause.lbd = lbd;
579 }
580
581 self.learned_clause_ids.push(clause_id);
583
584 if let Some(current_level_clauses) = self.assertion_clause_ids.last_mut() {
586 current_level_clauses.push(clause_id);
587 }
588
589 let lit0 = learnt_clause[0];
591 let lit1 = learnt_clause[1];
592 self.watches
593 .add(lit0.negate(), Watcher::new(clause_id, lit1));
594 self.watches
595 .add(lit1.negate(), Watcher::new(clause_id, lit0));
596
597 self.trail.assign_propagation(learnt_clause[0], clause_id);
599 }
600
601 self.vsids.decay();
603 self.chb.decay();
604 self.lrb.decay();
605 self.lrb.on_conflict();
606 self.clauses.decay_activity(self.config.clause_decay);
607 self.clause_bump_increment /= self.config.clause_decay;
609
610 self.conflicts_since_deletion += 1;
612
613 if self.conflicts_since_deletion >= self.config.clause_deletion_threshold as u64 {
615 self.reduce_clause_database();
616 self.conflicts_since_deletion = 0;
617
618 if self.stats.restarts.is_multiple_of(10) {
620 let saved_level = self.trail.decision_level();
621 if saved_level == 0 {
622 self.vivify_clauses();
623 }
624 }
625 }
626
627 if self.stats.conflicts >= self.restart_threshold {
629 self.restart();
630 }
631
632 if self.config.enable_inprocessing
634 && self.conflicts_since_inprocessing >= self.config.inprocessing_interval
635 {
636 self.inprocess();
637 self.conflicts_since_inprocessing = 0;
638 }
639 } else {
640 if let Some(var) = self.pick_branch_var() {
642 self.stats.decisions += 1;
643 self.trail.new_decision_level();
644
645 let polarity = if self.rand_bool(self.config.random_polarity_prob) {
647 self.rand_bool(0.5)
649 } else {
650 self.phase[var.index()]
652 };
653 let lit = if polarity {
654 Lit::pos(var)
655 } else {
656 Lit::neg(var)
657 };
658 self.trail.assign_decision(lit);
659 } else {
660 self.save_model();
662 return SolverResult::Sat;
663 }
664 }
665 }
666 }
667
668 pub fn solve_with_assumptions(
679 &mut self,
680 assumptions: &[Lit],
681 ) -> (SolverResult, Option<Vec<Lit>>) {
682 if self.trivially_unsat {
683 return (SolverResult::Unsat, Some(Vec::new()));
684 }
685
686 for &lit in assumptions {
688 while self.num_vars <= lit.var().index() {
689 self.new_var();
690 }
691 }
692
693 if self.propagate().is_some() {
695 return (SolverResult::Unsat, Some(Vec::new()));
696 }
697
698 let assumption_level_start = self.trail.decision_level();
700
701 for (i, &lit) in assumptions.iter().enumerate() {
703 let value = self.trail.lit_value(lit);
705 if value.is_true() {
706 continue; }
708 if value.is_false() {
709 let core = self.extract_assumption_core(assumptions, i);
711 self.backtrack(assumption_level_start);
712 return (SolverResult::Unsat, Some(core));
713 }
714
715 self.trail.new_decision_level();
717 self.trail.assign_decision(lit);
718
719 if let Some(_conflict) = self.propagate() {
721 let core = self.analyze_assumption_conflict(assumptions);
723 self.backtrack(assumption_level_start);
724 return (SolverResult::Unsat, Some(core));
725 }
726 }
727
728 loop {
730 if let Some(conflict) = self.propagate() {
731 self.stats.conflicts += 1;
732
733 let backtrack_level = self.analyze_conflict_level(conflict);
735
736 if backtrack_level <= assumption_level_start {
737 let core = self.analyze_assumption_conflict(assumptions);
739 self.backtrack(assumption_level_start);
740 return (SolverResult::Unsat, Some(core));
741 }
742
743 let (bt_level, learnt_clause) = self.analyze(conflict);
744 self.backtrack_with_phase_saving(bt_level.max(assumption_level_start + 1));
745 self.learn_clause(learnt_clause);
746
747 self.vsids.decay();
748 self.clauses.decay_activity(self.config.clause_decay);
749 self.handle_clause_deletion_and_restart_limited(assumption_level_start);
750 } else {
751 if let Some(var) = self.pick_branch_var() {
753 self.stats.decisions += 1;
754 self.trail.new_decision_level();
755
756 let polarity = if self.rand_bool(self.config.random_polarity_prob) {
757 self.rand_bool(0.5)
758 } else {
759 self.phase.get(var.index()).copied().unwrap_or(false)
760 };
761 let lit = if polarity {
762 Lit::pos(var)
763 } else {
764 Lit::neg(var)
765 };
766 self.trail.assign_decision(lit);
767 } else {
768 self.save_model();
770 self.backtrack(assumption_level_start);
771 return (SolverResult::Sat, None);
772 }
773 }
774 }
775 }
776
777 fn extract_assumption_core(&self, assumptions: &[Lit], conflict_idx: usize) -> Vec<Lit> {
779 let mut core = Vec::new();
781 let conflict_lit = assumptions[conflict_idx];
782
783 for &lit in &assumptions[..=conflict_idx] {
785 if self.seen.get(lit.var().index()).copied().unwrap_or(false) || lit == conflict_lit {
786 core.push(lit);
787 }
788 }
789
790 if core.is_empty() {
792 core.push(conflict_lit);
793 }
794
795 core
796 }
797
798 fn analyze_assumption_conflict(&mut self, assumptions: &[Lit]) -> Vec<Lit> {
800 let mut core = Vec::new();
802
803 for &lit in assumptions {
805 let var = lit.var();
806 if var.index() < self.trail.assignments().len() {
807 let value = self.trail.lit_value(lit);
808 if value.is_false() || self.seen.get(var.index()).copied().unwrap_or(false) {
810 core.push(lit);
811 }
812 }
813 }
814
815 if core.is_empty() {
817 core.extend(assumptions.iter().copied());
818 }
819
820 core
821 }
822
823 fn analyze_conflict_level(&self, conflict: ClauseId) -> u32 {
825 let clause = match self.clauses.get(conflict) {
826 Some(c) => c,
827 None => return 0,
828 };
829
830 let mut min_level = u32::MAX;
831 for lit in clause.lits.iter().copied() {
832 let level = self.trail.level(lit.var());
833 if level > 0 && level < min_level {
834 min_level = level;
835 }
836 }
837
838 if min_level == u32::MAX { 0 } else { min_level }
839 }
840
841 fn handle_clause_deletion_and_restart_limited(&mut self, min_level: u32) {
843 self.conflicts_since_deletion += 1;
844
845 if self.conflicts_since_deletion >= self.config.clause_deletion_threshold as u64 {
846 self.reduce_clause_database();
847 self.conflicts_since_deletion = 0;
848 }
849
850 if self.stats.conflicts >= self.restart_threshold {
851 self.backtrack(min_level);
853 self.stats.restarts += 1;
854 self.luby_index += 1;
855 self.restart_threshold =
856 self.stats.conflicts + self.config.restart_interval * Self::luby(self.luby_index);
857 }
858 }
859
860 fn propagate(&mut self) -> Option<ClauseId> {
862 while let Some(lit) = self.trail.next_to_propagate() {
863 self.stats.propagations += 1;
864
865 let binary_implications = self.binary_graph.get(lit).to_vec();
867 for &(implied_lit, clause_id) in &binary_implications {
868 let value = self.trail.lit_value(implied_lit);
869 if value.is_false() {
870 return Some(clause_id);
872 } else if !value.is_defined() {
873 self.trail.assign_propagation(implied_lit, clause_id);
875
876 if self.config.enable_lazy_hyper_binary {
878 self.check_hyper_binary_resolution(lit, implied_lit, clause_id);
879 }
880 }
881 }
882
883 let watches = std::mem::take(self.watches.get_mut(lit));
885
886 let mut i = 0;
887 while i < watches.len() {
888 let watcher = watches[i];
889
890 if self.trail.lit_value(watcher.blocker).is_true() {
892 i += 1;
893 continue;
894 }
895
896 let clause = match self.clauses.get_mut(watcher.clause) {
897 Some(c) if !c.deleted => c,
898 _ => {
899 i += 1;
900 continue;
901 }
902 };
903
904 if clause.lits[0] == lit.negate() {
906 clause.swap(0, 1);
907 }
908
909 let first = clause.lits[0];
911 if self.trail.lit_value(first).is_true() {
912 self.watches
914 .get_mut(lit)
915 .push(Watcher::new(watcher.clause, first));
916 i += 1;
917 continue;
918 }
919
920 let mut found = false;
922 for j in 2..clause.lits.len() {
923 let l = clause.lits[j];
924 if !self.trail.lit_value(l).is_false() {
925 clause.swap(1, j);
926 self.watches
927 .add(clause.lits[1].negate(), Watcher::new(watcher.clause, first));
928 found = true;
929 break;
930 }
931 }
932
933 if found {
934 i += 1;
935 continue;
936 }
937
938 self.watches
940 .get_mut(lit)
941 .push(Watcher::new(watcher.clause, first));
942
943 if self.trail.lit_value(first).is_false() {
944 for j in (i + 1)..watches.len() {
947 self.watches.get_mut(lit).push(watches[j]);
948 }
949 return Some(watcher.clause);
950 } else {
951 self.trail.assign_propagation(first, watcher.clause);
953
954 if self.config.enable_lazy_hyper_binary {
956 self.check_hyper_binary_resolution(lit, first, watcher.clause);
957 }
958 }
959
960 i += 1;
961 }
962 }
963
964 None
965 }
966
967 fn analyze(&mut self, conflict: ClauseId) -> (u32, SmallVec<[Lit; 16]>) {
969 self.learnt.clear();
970 self.learnt.push(Lit::from_code(0)); let mut counter = 0;
973 let mut p = None;
974 let mut index = self.trail.assignments().len();
975 let current_level = self.trail.decision_level();
976
977 for s in &mut self.seen {
979 *s = false;
980 }
981
982 let mut reason_clause = conflict;
983
984 loop {
985 let Some(clause) = self.clauses.get(reason_clause) else {
987 break; };
989 let start = if p.is_some() { 1 } else { 0 };
990 let is_learned = clause.learned;
991
992 if is_learned && let Some(clause_mut) = self.clauses.get_mut(reason_clause) {
994 clause_mut.record_usage();
995 if clause_mut.lbd <= 2 {
997 clause_mut.promote_to_core();
998 }
999 clause_mut.activity += self.clause_bump_increment;
1001 }
1002
1003 let Some(clause) = self.clauses.get(reason_clause) else {
1004 break;
1005 };
1006 for &lit in &clause.lits[start..] {
1007 let var = lit.var();
1008 let level = self.trail.level(var);
1009
1010 if !self.seen[var.index()] && level > 0 {
1011 self.seen[var.index()] = true;
1012 self.vsids.bump(var);
1013 self.chb.bump(var);
1014 self.lrb.on_reason(var);
1015
1016 if level == current_level {
1017 counter += 1;
1018 } else {
1019 self.learnt.push(lit.negate());
1020 }
1021 }
1022 }
1023
1024 let mut current_lit;
1026 loop {
1027 index -= 1;
1028 current_lit = self.trail.assignments()[index];
1029 p = Some(current_lit);
1030 if self.seen[current_lit.var().index()] {
1031 break;
1032 }
1033 }
1034
1035 counter -= 1;
1036 if counter == 0 {
1037 break;
1038 }
1039
1040 let var = current_lit.var();
1041 match self.trail.reason(var) {
1042 Reason::Propagation(c) => reason_clause = c,
1043 _ => break,
1044 }
1045 }
1046
1047 if let Some(lit) = p {
1049 self.learnt[0] = lit.negate();
1050 }
1051
1052 self.minimize_learnt_clause();
1054
1055 let assertion_level = if self.learnt.len() == 1 {
1057 0
1058 } else {
1059 let mut max_level = 0;
1061 let mut max_idx = 1;
1062 for (i, &lit) in self.learnt.iter().enumerate().skip(1) {
1063 let level = self.trail.level(lit.var());
1064 if level > max_level {
1065 max_level = level;
1066 max_idx = i;
1067 }
1068 }
1069 self.learnt.swap(1, max_idx);
1071 max_level
1072 };
1073
1074 let backtrack_level = self.chrono_backtrack.compute_backtrack_level(
1076 &self.trail,
1077 &self.learnt,
1078 assertion_level,
1079 );
1080
1081 if backtrack_level != assertion_level {
1083 self.stats.chrono_backtracks += 1;
1084 } else {
1085 self.stats.non_chrono_backtracks += 1;
1086 }
1087
1088 (backtrack_level, self.learnt.clone())
1089 }
1090
1091 fn pick_branch_var(&mut self) -> Option<Var> {
1093 if self.config.use_lrb_branching {
1094 while let Some(var) = self.lrb.select() {
1096 if !self.trail.is_assigned(var) {
1097 self.lrb.on_assign(var);
1098 return Some(var);
1099 }
1100 }
1101 } else if self.config.use_chb_branching {
1102 if self.stats.decisions.is_multiple_of(100) {
1105 self.chb.rebuild_heap();
1106 }
1107
1108 while let Some(var) = self.chb.pop_max() {
1109 if !self.trail.is_assigned(var) {
1110 return Some(var);
1111 }
1112 }
1113 } else {
1114 while let Some(var) = self.vsids.pop_max() {
1116 if !self.trail.is_assigned(var) {
1117 return Some(var);
1118 }
1119 }
1120 }
1121 None
1122 }
1123
1124 fn minimize_learnt_clause(&mut self) {
1135 if self.learnt.len() <= 2 {
1136 return;
1138 }
1139
1140 let original_len = self.learnt.len();
1141
1142 self.analyze_stack.clear();
1145
1146 let mut j = 1; for i in 1..self.learnt.len() {
1149 let lit = self.learnt[i];
1150 if self.lit_is_redundant(lit) {
1151 } else {
1153 self.learnt[j] = lit;
1155 j += 1;
1156 }
1157 }
1158 self.learnt.truncate(j);
1159
1160 self.strengthen_learnt_clause();
1164
1165 let final_len = self.learnt.len();
1167 if final_len < original_len {
1168 self.stats.minimizations += 1;
1169 self.stats.literals_removed += (original_len - final_len) as u64;
1170 }
1171 }
1172
1173 fn strengthen_learnt_clause(&mut self) {
1175 if self.learnt.len() <= 2 {
1176 return;
1177 }
1178
1179 let mut j = 1;
1181 for i in 1..self.learnt.len() {
1182 let lit = self.learnt[i];
1183 let var = lit.var();
1184
1185 if let Reason::Propagation(reason_id) = self.trail.reason(var)
1187 && let Some(reason_clause) = self.clauses.get(reason_id)
1188 && reason_clause.lits.len() == 2
1189 {
1190 let other_lit = if reason_clause.lits[0] == lit.negate() {
1192 reason_clause.lits[1]
1193 } else if reason_clause.lits[1] == lit.negate() {
1194 reason_clause.lits[0]
1195 } else {
1196 self.learnt[j] = lit;
1198 j += 1;
1199 continue;
1200 };
1201
1202 if self.trail.level(other_lit.var()) == 0 && self.seen[other_lit.var().index()] {
1205 continue;
1207 }
1208 }
1209
1210 self.learnt[j] = lit;
1212 j += 1;
1213 }
1214 self.learnt.truncate(j);
1215 }
1216
1217 fn lit_is_redundant(&mut self, lit: Lit) -> bool {
1224 let var = lit.var();
1225
1226 let reason = match self.trail.reason(var) {
1228 Reason::Decision => return false,
1229 Reason::Theory => return false, Reason::Propagation(c) => c,
1231 };
1232
1233 let reason_clause = match self.clauses.get(reason) {
1234 Some(c) => c,
1235 None => return false,
1236 };
1237
1238 for &reason_lit in &reason_clause.lits {
1240 if reason_lit == lit.negate() {
1241 continue;
1243 }
1244
1245 let reason_var = reason_lit.var();
1246
1247 if self.trail.level(reason_var) == 0 {
1249 continue;
1250 }
1251
1252 if self.seen[reason_var.index()] {
1254 continue;
1255 }
1256
1257 return false;
1260 }
1261
1262 true
1263 }
1264
1265 fn backtrack_with_phase_saving(&mut self, level: u32) {
1267 let phase = &mut self.phase;
1269 let lrb = &mut self.lrb;
1270 self.trail.backtrack_to_with_callback(level, |lit| {
1271 let var = lit.var();
1272 if var.index() < phase.len() {
1273 phase[var.index()] = lit.is_pos();
1274 }
1275 lrb.unassign(var);
1277 });
1278 }
1279
1280 fn backtrack(&mut self, level: u32) {
1282 self.trail.backtrack_to(level);
1283 }
1284
1285 fn luby(i: u64) -> u64 {
1289 let i = i + 1; let mut k = 1u32;
1293 while (1u64 << k) - 1 < i {
1294 k += 1;
1295 }
1296
1297 let seq_len = (1u64 << k) - 1;
1298
1299 if i == seq_len {
1300 1u64 << (k - 1)
1302 } else {
1303 let half_len = (1u64 << (k - 1)) - 1;
1306 if i <= half_len {
1307 Self::luby(i - 1) } else if i <= 2 * half_len {
1309 Self::luby(i - half_len - 1)
1310 } else {
1311 1u64 << (k - 1)
1312 }
1313 }
1314 }
1315
1316 fn restart(&mut self) {
1318 self.stats.restarts += 1;
1319 self.backtrack_with_phase_saving(0);
1320
1321 match self.config.restart_strategy {
1323 RestartStrategy::Luby => {
1324 self.luby_index += 1;
1325 self.restart_threshold = self.stats.conflicts
1326 + Self::luby(self.luby_index) * self.config.restart_interval;
1327 }
1328 RestartStrategy::Geometric => {
1329 let current_interval = if self.restart_threshold > self.stats.conflicts {
1330 self.restart_threshold - self.stats.conflicts
1331 } else {
1332 self.config.restart_interval
1333 };
1334 let next_interval =
1335 (current_interval as f64 * self.config.restart_multiplier) as u64;
1336 self.restart_threshold = self.stats.conflicts + next_interval;
1337 }
1338 RestartStrategy::Glucose => {
1339 let current_interval = if self.restart_threshold > self.stats.conflicts {
1343 self.restart_threshold - self.stats.conflicts
1344 } else {
1345 self.config.restart_interval
1346 };
1347
1348 let next_interval = if self.recent_lbd_count > 50 {
1350 let recent_avg = self.recent_lbd_sum / self.recent_lbd_count.max(1);
1351 if recent_avg < 5 {
1353 ((current_interval as f64) * 1.1) as u64
1355 } else {
1356 ((current_interval as f64) * 0.9) as u64
1358 }
1359 } else {
1360 current_interval
1361 };
1362
1363 self.restart_threshold = self.stats.conflicts + next_interval.max(100);
1364 }
1365 RestartStrategy::LocalLbd => {
1366 self.conflicts_since_local_restart += 1;
1369
1370 if self.conflicts_since_local_restart >= 50 && self.should_local_restart() {
1371 let local_level = self.compute_local_restart_level();
1373 self.backtrack_with_phase_saving(local_level);
1374 self.conflicts_since_local_restart = 0;
1375 self.recent_lbd_sum = 0;
1377 self.recent_lbd_count = 0;
1378 } else {
1379 let current_interval = if self.restart_threshold > self.stats.conflicts {
1381 self.restart_threshold - self.stats.conflicts
1382 } else {
1383 self.config.restart_interval
1384 };
1385 self.restart_threshold = self.stats.conflicts + current_interval;
1386 }
1387 return; }
1389 }
1390
1391 for i in 0..self.num_vars {
1393 let var = Var::new(i as u32);
1394 if !self.trail.is_assigned(var) && !self.vsids.contains(var) {
1395 self.vsids.insert(var);
1396 }
1397 }
1398 }
1399
1400 fn should_local_restart(&self) -> bool {
1403 if self.recent_lbd_count < 50 || self.global_lbd_count < 100 {
1404 return false;
1405 }
1406
1407 let recent_avg = self.recent_lbd_sum / self.recent_lbd_count.max(1);
1408 let global_avg = self.global_lbd_sum / self.global_lbd_count.max(1);
1409
1410 recent_avg * 4 > global_avg * 5
1412 }
1413
1414 fn compute_local_restart_level(&self) -> u32 {
1417 let current_level = self.trail.decision_level();
1418
1419 if current_level > 5 {
1421 current_level / 5
1422 } else {
1423 0
1424 }
1425 }
1426
1427 fn compute_lbd(&mut self, lits: &[Lit]) -> u32 {
1430 self.lbd_mark += 1;
1431 let mark = self.lbd_mark;
1432
1433 let mut count = 0u32;
1434 for &lit in lits {
1435 let level = self.trail.level(lit.var()) as usize;
1436 if level < self.level_marks.len() && self.level_marks[level] != mark {
1437 self.level_marks[level] = mark;
1438 count += 1;
1439 }
1440 }
1441
1442 count
1443 }
1444
1445 fn reduce_clause_database(&mut self) {
1450 use crate::clause::ClauseTier;
1451
1452 let mut core_candidates: Vec<(ClauseId, f64)> = Vec::new();
1453 let mut mid_candidates: Vec<(ClauseId, f64)> = Vec::new();
1454 let mut local_candidates: Vec<(ClauseId, f64)> = Vec::new();
1455
1456 for &cid in &self.learned_clause_ids {
1457 if let Some(clause) = self.clauses.get(cid) {
1458 if clause.deleted {
1459 continue;
1460 }
1461
1462 if clause.lits.len() <= 2 {
1464 continue;
1465 }
1466
1467 let is_reason = clause.lits.iter().any(|&lit| {
1470 let var = lit.var();
1471 if self.trail.is_assigned(var) {
1472 matches!(self.trail.reason(var), Reason::Propagation(r) if r == cid)
1473 } else {
1474 false
1475 }
1476 });
1477
1478 if !is_reason {
1479 match clause.tier {
1480 ClauseTier::Core => core_candidates.push((cid, clause.activity)),
1481 ClauseTier::Mid => mid_candidates.push((cid, clause.activity)),
1482 ClauseTier::Local => local_candidates.push((cid, clause.activity)),
1483 }
1484 }
1485 }
1486 }
1487
1488 core_candidates.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
1490 mid_candidates.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
1491 local_candidates.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
1492
1493 let num_core_delete = core_candidates.len() / 10;
1496 let num_mid_delete = (mid_candidates.len() * 3) / 10;
1498 let num_local_delete = (local_candidates.len() * 3) / 4;
1500
1501 for (cid, _) in core_candidates.iter().take(num_core_delete) {
1502 self.clauses.remove(*cid);
1503 self.stats.deleted_clauses += 1;
1504 }
1505
1506 for (cid, _) in mid_candidates.iter().take(num_mid_delete) {
1507 self.clauses.remove(*cid);
1508 self.stats.deleted_clauses += 1;
1509 }
1510
1511 for (cid, _) in local_candidates.iter().take(num_local_delete) {
1512 self.clauses.remove(*cid);
1513 self.stats.deleted_clauses += 1;
1514 }
1515
1516 self.learned_clause_ids
1518 .retain(|&cid| self.clauses.get(cid).is_some_and(|c| !c.deleted));
1519 }
1520
1521 fn save_model(&mut self) {
1523 self.model.resize(self.num_vars, LBool::Undef);
1524 for i in 0..self.num_vars {
1525 self.model[i] = self.trail.value(Var::new(i as u32));
1526 }
1527 }
1528
1529 #[must_use]
1531 pub fn model(&self) -> &[LBool] {
1532 &self.model
1533 }
1534
1535 #[must_use]
1537 pub fn model_value(&self, var: Var) -> LBool {
1538 self.model.get(var.index()).copied().unwrap_or(LBool::Undef)
1539 }
1540
1541 #[must_use]
1543 pub fn stats(&self) -> &SolverStats {
1544 &self.stats
1545 }
1546
1547 #[must_use]
1549 pub fn num_vars(&self) -> usize {
1550 self.num_vars
1551 }
1552
1553 #[must_use]
1555 pub fn num_clauses(&self) -> usize {
1556 self.clauses.len()
1557 }
1558
1559 pub fn push(&mut self) {
1565 self.trail.backtrack_to(0);
1568
1569 self.assertion_levels.push(self.clauses.num_original());
1570 self.assertion_trail_sizes.push(self.trail.size());
1571 self.assertion_clause_ids.push(Vec::new());
1572 }
1573
1574 pub fn pop(&mut self) {
1576 if self.assertion_levels.len() > 1 {
1577 self.assertion_levels.pop();
1578
1579 let trail_size = self.assertion_trail_sizes.pop().unwrap_or(0);
1581
1582 if let Some(clause_ids_to_remove) = self.assertion_clause_ids.pop() {
1584 for clause_id in clause_ids_to_remove {
1585 self.clauses.remove(clause_id);
1587
1588 self.learned_clause_ids.retain(|&id| id != clause_id);
1590
1591 }
1594 }
1595
1596 self.trail.backtrack_to_size(trail_size);
1599
1600 self.trail.backtrack_to(0);
1602
1603 self.trivially_unsat = false;
1605 }
1606 }
1607
1608 pub fn backtrack_to_root(&mut self) {
1613 self.trail.backtrack_to(0);
1614 }
1615
1616 pub fn reset(&mut self) {
1618 self.clauses = ClauseDatabase::new();
1619 self.trail.clear();
1620 self.watches.clear();
1621 self.vsids.clear();
1622 self.chb.clear();
1623 self.stats = SolverStats::default();
1624 self.learnt.clear();
1625 self.seen.clear();
1626 self.analyze_stack.clear();
1627 self.assertion_levels.clear();
1628 self.assertion_levels.push(0);
1629 self.assertion_trail_sizes.clear();
1630 self.assertion_trail_sizes.push(0);
1631 self.assertion_clause_ids.clear();
1632 self.assertion_clause_ids.push(Vec::new());
1633 self.model.clear();
1634 self.num_vars = 0;
1635 self.restart_threshold = self.config.restart_interval;
1636 self.trivially_unsat = false;
1637 self.phase.clear();
1638 self.luby_index = 0;
1639 self.level_marks.clear();
1640 self.lbd_mark = 0;
1641 self.learned_clause_ids.clear();
1642 self.conflicts_since_deletion = 0;
1643 self.rng_state = 0x853c_49e6_748f_ea9b;
1644 self.recent_lbd_sum = 0;
1645 self.recent_lbd_count = 0;
1646 self.binary_graph.clear();
1647 self.global_lbd_sum = 0;
1648 self.global_lbd_count = 0;
1649 self.conflicts_since_local_restart = 0;
1650 }
1651
1652 pub fn solve_with_theory<T: TheoryCallback>(&mut self, theory: &mut T) -> SolverResult {
1661 if self.trivially_unsat {
1662 return SolverResult::Unsat;
1663 }
1664
1665 if self.propagate().is_some() {
1667 return SolverResult::Unsat;
1668 }
1669
1670 loop {
1671 if let Some(conflict) = self.propagate() {
1673 self.stats.conflicts += 1;
1674
1675 if self.trail.decision_level() == 0 {
1676 return SolverResult::Unsat;
1677 }
1678
1679 let (backtrack_level, learnt_clause) = self.analyze(conflict);
1680 theory.on_backtrack(backtrack_level);
1681 self.backtrack_with_phase_saving(backtrack_level);
1682 self.learn_clause(learnt_clause);
1683
1684 self.vsids.decay();
1685 self.clauses.decay_activity(self.config.clause_decay);
1686 self.handle_clause_deletion_and_restart();
1687 continue;
1688 }
1689
1690 loop {
1692 let assignments = self.trail.assignments().to_vec();
1694 let mut theory_conflict = None;
1695 let mut theory_propagations = Vec::new();
1696
1697 for &lit in &assignments {
1699 match theory.on_assignment(lit) {
1700 TheoryCheckResult::Sat => {}
1701 TheoryCheckResult::Conflict(conflict_lits) => {
1702 theory_conflict = Some(conflict_lits);
1703 break;
1704 }
1705 TheoryCheckResult::Propagated(props) => {
1706 theory_propagations.extend(props);
1707 }
1708 }
1709 }
1710
1711 if let Some(conflict_lits) = theory_conflict {
1713 self.stats.conflicts += 1;
1714
1715 if self.trail.decision_level() == 0 {
1716 return SolverResult::Unsat;
1717 }
1718
1719 let (backtrack_level, learnt_clause) =
1720 self.analyze_theory_conflict(&conflict_lits);
1721 theory.on_backtrack(backtrack_level);
1722 self.backtrack_with_phase_saving(backtrack_level);
1723 self.learn_clause(learnt_clause);
1724
1725 self.vsids.decay();
1726 self.clauses.decay_activity(self.config.clause_decay);
1727 self.handle_clause_deletion_and_restart();
1728 continue;
1729 }
1730
1731 let mut made_propagation = false;
1733 for (lit, reason_lits) in theory_propagations {
1734 if !self.trail.is_assigned(lit.var()) {
1735 let clause_id = self.add_theory_reason_clause(&reason_lits, lit);
1737 self.trail.assign_propagation(lit, clause_id);
1738 made_propagation = true;
1739 }
1740 }
1741
1742 if made_propagation {
1743 if let Some(conflict) = self.propagate() {
1745 self.stats.conflicts += 1;
1746
1747 if self.trail.decision_level() == 0 {
1748 return SolverResult::Unsat;
1749 }
1750
1751 let (backtrack_level, learnt_clause) = self.analyze(conflict);
1752 theory.on_backtrack(backtrack_level);
1753 self.backtrack_with_phase_saving(backtrack_level);
1754 self.learn_clause(learnt_clause);
1755
1756 self.vsids.decay();
1757 self.clauses.decay_activity(self.config.clause_decay);
1758 self.handle_clause_deletion_and_restart();
1759 }
1760 continue;
1761 }
1762
1763 break;
1764 }
1765
1766 if let Some(var) = self.pick_branch_var() {
1768 self.stats.decisions += 1;
1769 self.trail.new_decision_level();
1770 let new_level = self.trail.decision_level();
1771 theory.on_new_level(new_level);
1772
1773 let polarity = if self.rand_bool(self.config.random_polarity_prob) {
1774 self.rand_bool(0.5)
1775 } else {
1776 self.phase[var.index()]
1777 };
1778 let lit = if polarity {
1779 Lit::pos(var)
1780 } else {
1781 Lit::neg(var)
1782 };
1783 self.trail.assign_decision(lit);
1784 } else {
1785 match theory.final_check() {
1787 TheoryCheckResult::Sat => {
1788 self.save_model();
1789 return SolverResult::Sat;
1790 }
1791 TheoryCheckResult::Conflict(conflict_lits) => {
1792 self.stats.conflicts += 1;
1793
1794 if self.trail.decision_level() == 0 {
1795 return SolverResult::Unsat;
1796 }
1797
1798 let (backtrack_level, learnt_clause) =
1799 self.analyze_theory_conflict(&conflict_lits);
1800 theory.on_backtrack(backtrack_level);
1801 self.backtrack_with_phase_saving(backtrack_level);
1802 self.learn_clause(learnt_clause);
1803
1804 self.vsids.decay();
1805 self.clauses.decay_activity(self.config.clause_decay);
1806 self.handle_clause_deletion_and_restart();
1807 }
1808 TheoryCheckResult::Propagated(props) => {
1809 for (lit, reason_lits) in props {
1811 if !self.trail.is_assigned(lit.var()) {
1812 let clause_id = self.add_theory_reason_clause(&reason_lits, lit);
1813 self.trail.assign_propagation(lit, clause_id);
1814 }
1815 }
1816 }
1817 }
1818 }
1819 }
1820 }
1821
1822 fn analyze_theory_conflict(&mut self, conflict_lits: &[Lit]) -> (u32, SmallVec<[Lit; 16]>) {
1824 self.learnt.clear();
1825 self.learnt.push(Lit::from_code(0)); let mut counter = 0;
1828 let current_level = self.trail.decision_level();
1829
1830 for s in &mut self.seen {
1832 *s = false;
1833 }
1834
1835 for &lit in conflict_lits {
1837 let var = lit.var();
1838 let level = self.trail.level(var);
1839
1840 if !self.seen[var.index()] && level > 0 {
1841 self.seen[var.index()] = true;
1842 self.vsids.bump(var);
1843 self.chb.bump(var);
1844
1845 if level == current_level {
1846 counter += 1;
1847 } else {
1848 self.learnt.push(lit.negate());
1849 }
1850 }
1851 }
1852
1853 let mut index = self.trail.assignments().len();
1855 let mut p = None;
1856
1857 while counter > 0 {
1858 index -= 1;
1859 let current_lit = self.trail.assignments()[index];
1860 p = Some(current_lit);
1861 let var = current_lit.var();
1862
1863 if self.seen[var.index()] {
1864 counter -= 1;
1865
1866 if counter > 0
1867 && let Reason::Propagation(reason_clause) = self.trail.reason(var)
1868 && let Some(clause) = self.clauses.get(reason_clause)
1869 {
1870 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 if let Some(uip) = p {
1893 self.learnt[0] = uip.negate();
1894 }
1895
1896 self.minimize_learnt_clause();
1898
1899 let backtrack_level = if self.learnt.len() == 1 {
1901 0
1902 } else {
1903 let mut max_level = 0;
1904 let mut max_idx = 1;
1905 for (i, &lit) in self.learnt.iter().enumerate().skip(1) {
1906 let level = self.trail.level(lit.var());
1907 if level > max_level {
1908 max_level = level;
1909 max_idx = i;
1910 }
1911 }
1912 self.learnt.swap(1, max_idx);
1913 max_level
1914 };
1915
1916 (backtrack_level, self.learnt.clone())
1917 }
1918
1919 fn add_theory_reason_clause(&mut self, reason_lits: &[Lit], propagated_lit: Lit) -> ClauseId {
1922 let mut clause_lits: SmallVec<[Lit; 8]> = SmallVec::new();
1923 clause_lits.push(propagated_lit);
1924 for &lit in reason_lits {
1925 clause_lits.push(lit.negate());
1926 }
1927
1928 let clause_id = self.clauses.add_learned(clause_lits.iter().copied());
1929
1930 if clause_lits.len() >= 2 {
1932 let lit0 = clause_lits[0];
1933 let lit1 = clause_lits[1];
1934 self.watches
1935 .add(lit0.negate(), Watcher::new(clause_id, lit1));
1936 self.watches
1937 .add(lit1.negate(), Watcher::new(clause_id, lit0));
1938 }
1939
1940 clause_id
1941 }
1942
1943 fn learn_clause(&mut self, learnt_clause: SmallVec<[Lit; 16]>) {
1946 if learnt_clause.len() == 1 {
1947 self.trail.assign_decision(learnt_clause[0]);
1948 self.stats.unit_clauses += 1;
1949 self.stats.learned_clauses += 1;
1950 } else if learnt_clause.len() == 2 {
1951 let lbd = self.compute_lbd(&learnt_clause);
1953 let clause_id = self.clauses.add_learned(learnt_clause.iter().copied());
1954 self.stats.learned_clauses += 1;
1955 self.stats.binary_clauses += 1;
1956 self.stats.total_lbd += lbd as u64;
1957
1958 if let Some(clause) = self.clauses.get_mut(clause_id) {
1959 clause.lbd = lbd;
1960 }
1961
1962 self.learned_clause_ids.push(clause_id);
1963
1964 let lit0 = learnt_clause[0];
1965 let lit1 = learnt_clause[1];
1966
1967 self.binary_graph.add(lit0.negate(), lit1, clause_id);
1969 self.binary_graph.add(lit1.negate(), lit0, clause_id);
1970
1971 self.watches
1972 .add(lit0.negate(), Watcher::new(clause_id, lit1));
1973 self.watches
1974 .add(lit1.negate(), Watcher::new(clause_id, lit0));
1975
1976 self.trail.assign_propagation(learnt_clause[0], clause_id);
1977 } else {
1978 let lbd = self.compute_lbd(&learnt_clause);
1979 self.stats.total_lbd += lbd as u64;
1980 let clause_id = self.clauses.add_learned(learnt_clause.iter().copied());
1981 self.stats.learned_clauses += 1;
1982
1983 if let Some(clause) = self.clauses.get_mut(clause_id) {
1984 clause.lbd = lbd;
1985 }
1986
1987 self.learned_clause_ids.push(clause_id);
1988
1989 let lit0 = learnt_clause[0];
1990 let lit1 = learnt_clause[1];
1991 self.watches
1992 .add(lit0.negate(), Watcher::new(clause_id, lit1));
1993 self.watches
1994 .add(lit1.negate(), Watcher::new(clause_id, lit0));
1995
1996 self.trail.assign_propagation(learnt_clause[0], clause_id);
1997
1998 if learnt_clause.len() <= 5 && lbd <= 3 {
2000 self.check_subsumption(clause_id);
2001 }
2002 }
2003 }
2004
2005 fn check_subsumption(&mut self, new_clause_id: ClauseId) {
2008 let new_clause = match self.clauses.get(new_clause_id) {
2009 Some(c) => c.lits.clone(),
2010 None => return,
2011 };
2012
2013 if new_clause.len() > 10 {
2014 return; }
2016
2017 let mut to_remove = Vec::new();
2019 for &cid in &self.learned_clause_ids {
2020 if cid == new_clause_id {
2021 continue;
2022 }
2023
2024 if let Some(clause) = self.clauses.get(cid) {
2025 if clause.deleted || clause.lits.len() < new_clause.len() {
2026 continue;
2027 }
2028
2029 if new_clause.iter().all(|&lit| clause.lits.contains(&lit)) {
2031 to_remove.push(cid);
2032 }
2033 }
2034 }
2035
2036 for cid in to_remove {
2038 self.clauses.remove(cid);
2039 self.stats.deleted_clauses += 1;
2040 }
2041 }
2042
2043 fn handle_clause_deletion_and_restart(&mut self) {
2045 self.conflicts_since_deletion += 1;
2046
2047 if self.conflicts_since_deletion >= self.config.clause_deletion_threshold as u64 {
2048 self.reduce_clause_database();
2049 self.conflicts_since_deletion = 0;
2050 }
2051
2052 if self.stats.conflicts >= self.restart_threshold {
2053 self.restart();
2054 }
2055 }
2056
2057 #[must_use]
2059 pub fn trail(&self) -> &Trail {
2060 &self.trail
2061 }
2062
2063 #[must_use]
2065 pub fn decision_level(&self) -> u32 {
2066 self.trail.decision_level()
2067 }
2068
2069 fn rand_u64(&mut self) -> u64 {
2071 let mut x = self.rng_state;
2072 x ^= x << 13;
2073 x ^= x >> 7;
2074 x ^= x << 17;
2075 self.rng_state = x;
2076 x
2077 }
2078
2079 fn rand_f64(&mut self) -> f64 {
2081 const MAX: f64 = u64::MAX as f64;
2082 (self.rand_u64() as f64) / MAX
2083 }
2084
2085 fn rand_bool(&mut self, probability: f64) -> bool {
2087 self.rand_f64() < probability
2088 }
2089
2090 fn check_hyper_binary_resolution(&mut self, _lit: Lit, implied: Lit, reason_id: ClauseId) {
2094 if self.trail.decision_level() < 2 {
2096 return;
2097 }
2098
2099 let reason_clause = match self.clauses.get(reason_id) {
2101 Some(c) if c.lits.len() >= 2 && c.lits.len() <= 4 => c.lits.clone(),
2102 _ => return,
2103 };
2104
2105 let current_level = self.trail.decision_level();
2108 let mut current_level_lits = SmallVec::<[Lit; 4]>::new();
2109
2110 for &reason_lit in &reason_clause {
2111 if reason_lit != implied {
2112 let var = reason_lit.var();
2113 if self.trail.level(var) == current_level {
2114 current_level_lits.push(reason_lit);
2115 }
2116 }
2117 }
2118
2119 if current_level_lits.len() == 1 {
2122 let other_lit = current_level_lits[0];
2123
2124 let binary_clause_lits = [other_lit.negate(), implied];
2127
2128 if !self.has_binary_implication(other_lit, implied) {
2130 let clause_id = self.clauses.add_learned(binary_clause_lits.iter().copied());
2132 self.binary_graph.add(other_lit, implied, clause_id);
2133 self.binary_graph
2134 .add(implied.negate(), other_lit.negate(), clause_id);
2135 self.stats.learned_clauses += 1;
2136 }
2137 }
2138 }
2139
2140 fn has_binary_implication(&self, from_lit: Lit, to_lit: Lit) -> bool {
2142 self.binary_graph
2143 .get(from_lit)
2144 .iter()
2145 .any(|(lit, _)| *lit == to_lit)
2146 }
2147
2148 fn vivify_clauses(&mut self) {
2151 if self.trail.decision_level() != 0 {
2152 return; }
2154
2155 let mut vivified_count = 0;
2156 let max_vivifications = 100; let clause_ids: Vec<ClauseId> = self
2160 .learned_clause_ids
2161 .iter()
2162 .copied()
2163 .take(max_vivifications)
2164 .collect();
2165
2166 for clause_id in clause_ids {
2167 if vivified_count >= max_vivifications {
2168 break;
2169 }
2170
2171 let clause_lits = match self.clauses.get(clause_id) {
2172 Some(c) if !c.deleted && c.lits.len() > 2 => c.lits.clone(),
2173 _ => continue,
2174 };
2175
2176 for skip_idx in 0..clause_lits.len() {
2179 let saved_level = self.trail.decision_level();
2181
2182 self.trail.new_decision_level();
2184 let mut conflict = false;
2185
2186 for (i, &lit) in clause_lits.iter().enumerate() {
2187 if i == skip_idx {
2188 continue;
2189 }
2190
2191 let value = self.trail.lit_value(lit);
2192 if value.is_true() {
2193 conflict = false;
2195 break;
2196 } else if value.is_false() {
2197 continue;
2199 } else {
2200 self.trail.assign_decision(lit.negate());
2202
2203 if self.propagate().is_some() {
2205 conflict = true;
2206 break;
2207 }
2208 }
2209 }
2210
2211 self.backtrack(saved_level);
2213
2214 if conflict
2215 && let Some(clause) = self.clauses.get_mut(clause_id)
2216 && clause.lits.len() > 2
2217 {
2218 clause.lits.remove(skip_idx);
2221 vivified_count += 1;
2222 break; }
2224 }
2225 }
2226 }
2227
2228 fn inprocess(&mut self) {
2230 use crate::preprocessing::Preprocessor;
2231
2232 if self.trail.decision_level() != 0 {
2234 return;
2235 }
2236
2237 let mut preprocessor = Preprocessor::new(self.num_vars);
2239
2240 let _pure_elim = preprocessor.pure_literal_elimination(&mut self.clauses);
2242 let _subsumption = preprocessor.subsumption_elimination(&mut self.clauses);
2243
2244 self.strengthen_clauses_inprocessing();
2246
2247 }
2251
2252 fn strengthen_clauses_inprocessing(&mut self) {
2257 if self.trail.decision_level() != 0 {
2258 return;
2259 }
2260
2261 let max_clauses_to_strengthen = 50; let mut strengthened_count = 0;
2263
2264 let mut candidates: Vec<(ClauseId, u32)> = Vec::new();
2266
2267 for &clause_id in &self.learned_clause_ids {
2268 if let Some(clause) = self.clauses.get(clause_id)
2269 && !clause.deleted
2270 && clause.lits.len() > 3
2271 && clause.lbd > 2
2272 {
2273 candidates.push((clause_id, clause.lbd));
2274 }
2275 }
2276
2277 candidates.sort_by_key(|(_, lbd)| std::cmp::Reverse(*lbd));
2279
2280 for (clause_id, _) in candidates.iter().take(max_clauses_to_strengthen) {
2281 if strengthened_count >= max_clauses_to_strengthen {
2282 break;
2283 }
2284
2285 let clause_lits = match self.clauses.get(*clause_id) {
2286 Some(c) if !c.deleted && c.lits.len() > 3 => c.lits.clone(),
2287 _ => continue,
2288 };
2289
2290 let mut literals_to_remove = Vec::new();
2292
2293 for (i, &lit) in clause_lits.iter().enumerate() {
2294 let saved_level = self.trail.decision_level();
2296
2297 self.trail.new_decision_level();
2299 self.trail.assign_decision(lit.negate());
2300
2301 let conflict = self.propagate();
2303
2304 self.backtrack(saved_level);
2306
2307 if conflict.is_some() {
2308 let mut remaining: Vec<Lit> = clause_lits
2315 .iter()
2316 .enumerate()
2317 .filter(|(j, _)| *j != i)
2318 .map(|(_, &l)| l)
2319 .collect();
2320
2321 if remaining.len() >= 2 {
2323 remaining.sort_by_key(|l| l.code());
2325 let mut is_tautology = false;
2326 for k in 0..remaining.len() - 1 {
2327 if remaining[k] == remaining[k + 1].negate() {
2328 is_tautology = true;
2329 break;
2330 }
2331 }
2332
2333 if !is_tautology {
2334 literals_to_remove.push(i);
2335 break; }
2337 }
2338 }
2339 }
2340
2341 if !literals_to_remove.is_empty() {
2343 if let Some(clause) = self.clauses.get_mut(*clause_id) {
2345 for &idx in literals_to_remove.iter().rev() {
2347 if idx < clause.lits.len() {
2348 clause.lits.remove(idx);
2349 }
2350 }
2351 }
2352
2353 if let Some(clause) = self.clauses.get(*clause_id) {
2355 let lits_clone = clause.lits.clone();
2356 let new_lbd = self.compute_lbd(&lits_clone);
2357
2358 if let Some(clause) = self.clauses.get_mut(*clause_id) {
2360 clause.lbd = new_lbd;
2361 }
2362
2363 strengthened_count += 1;
2364 }
2365 }
2366 }
2367 }
2368}
2369
2370#[cfg(test)]
2371mod tests {
2372 use super::*;
2373
2374 #[test]
2375 fn test_empty_sat() {
2376 let mut solver = Solver::new();
2377 assert_eq!(solver.solve(), SolverResult::Sat);
2378 }
2379
2380 #[test]
2381 fn test_simple_sat() {
2382 let mut solver = Solver::new();
2383 let _x = solver.new_var();
2384 let _y = solver.new_var();
2385
2386 solver.add_clause_dimacs(&[1, 2]);
2388 solver.add_clause_dimacs(&[-1, 2]);
2390
2391 assert_eq!(solver.solve(), SolverResult::Sat);
2392 assert!(solver.model_value(Var::new(1)).is_true()); }
2394
2395 #[test]
2396 fn test_simple_unsat() {
2397 let mut solver = Solver::new();
2398 let _x = solver.new_var();
2399
2400 solver.add_clause_dimacs(&[1]);
2402 solver.add_clause_dimacs(&[-1]);
2404
2405 assert_eq!(solver.solve(), SolverResult::Unsat);
2406 }
2407
2408 #[test]
2409 fn test_pigeonhole_2_1() {
2410 let mut solver = Solver::new();
2412 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);
2423 }
2424
2425 #[test]
2426 fn test_3sat_random() {
2427 let mut solver = Solver::new();
2428 for _ in 0..10 {
2429 solver.new_var();
2430 }
2431
2432 solver.add_clause_dimacs(&[1, 2, 3]);
2434 solver.add_clause_dimacs(&[-1, 4, 5]);
2435 solver.add_clause_dimacs(&[2, -3, 6]);
2436 solver.add_clause_dimacs(&[-4, 7, 8]);
2437 solver.add_clause_dimacs(&[5, -6, 9]);
2438 solver.add_clause_dimacs(&[-7, 8, 10]);
2439 solver.add_clause_dimacs(&[1, -8, -9]);
2440 solver.add_clause_dimacs(&[-2, 3, -10]);
2441
2442 let result = solver.solve();
2443 assert_eq!(result, SolverResult::Sat);
2444 }
2445
2446 #[test]
2447 fn test_luby_sequence() {
2448 assert_eq!(Solver::luby(0), 1);
2450 assert_eq!(Solver::luby(1), 1);
2451 assert_eq!(Solver::luby(2), 2);
2452 assert_eq!(Solver::luby(3), 1);
2453 assert_eq!(Solver::luby(4), 1);
2454 assert_eq!(Solver::luby(5), 2);
2455 assert_eq!(Solver::luby(6), 4);
2456 assert_eq!(Solver::luby(7), 1);
2457 }
2458
2459 #[test]
2460 fn test_phase_saving() {
2461 let mut solver = Solver::new();
2462 for _ in 0..5 {
2463 solver.new_var();
2464 }
2465
2466 solver.add_clause_dimacs(&[1, 2]);
2468 solver.add_clause_dimacs(&[-1, 3]);
2469 solver.add_clause_dimacs(&[-2, 4]);
2470 solver.add_clause_dimacs(&[-3, -4, 5]);
2471 solver.add_clause_dimacs(&[-5, 1]);
2472
2473 let result = solver.solve();
2474 assert_eq!(result, SolverResult::Sat);
2475 }
2476
2477 #[test]
2478 fn test_lbd_computation() {
2479 let mut solver = Solver::with_config(SolverConfig {
2481 clause_deletion_threshold: 5, ..SolverConfig::default()
2483 });
2484
2485 for _ in 0..20 {
2486 solver.new_var();
2487 }
2488
2489 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();
2508 assert_eq!(result, SolverResult::Unsat);
2509 assert!(solver.stats().conflicts > 0);
2511 }
2512
2513 #[test]
2514 fn test_clause_activity_decay() {
2515 let mut solver = Solver::new();
2516 for _ in 0..10 {
2517 solver.new_var();
2518 }
2519
2520 solver.add_clause_dimacs(&[1, 2, 3]);
2522 solver.add_clause_dimacs(&[-1, 4, 5]);
2523 solver.add_clause_dimacs(&[-2, -3, 6]);
2524
2525 let result = solver.solve();
2527 assert_eq!(result, SolverResult::Sat);
2528 }
2529
2530 #[test]
2531 fn test_clause_minimization() {
2532 let mut solver = Solver::new();
2535
2536 for _ in 0..15 {
2537 solver.new_var();
2538 }
2539
2540 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]);
2557 solver.add_clause_dimacs(&[-2, -12]);
2558 solver.add_clause_dimacs(&[-7, -12]);
2559
2560 solver.add_clause_dimacs(&[-3, -8]);
2561 solver.add_clause_dimacs(&[-3, -13]);
2562 solver.add_clause_dimacs(&[-8, -13]);
2563
2564 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]);
2571 solver.add_clause_dimacs(&[-12, -13]);
2572
2573 let result = solver.solve();
2574 assert_eq!(result, SolverResult::Sat);
2575
2576 assert!(solver.stats().conflicts > 0 || solver.stats().learned_clauses > 0);
2578 }
2579
2580 struct NullTheory;
2582
2583 impl TheoryCallback for NullTheory {
2584 fn on_assignment(&mut self, _lit: Lit) -> TheoryCheckResult {
2585 TheoryCheckResult::Sat
2586 }
2587
2588 fn final_check(&mut self) -> TheoryCheckResult {
2589 TheoryCheckResult::Sat
2590 }
2591
2592 fn on_backtrack(&mut self, _level: u32) {}
2593 }
2594
2595 #[test]
2596 fn test_solve_with_theory_sat() {
2597 let mut solver = Solver::new();
2598 let mut theory = NullTheory;
2599
2600 let _x = solver.new_var();
2601 let _y = solver.new_var();
2602
2603 solver.add_clause_dimacs(&[1, 2]);
2605 solver.add_clause_dimacs(&[-1, 2]);
2607
2608 assert_eq!(solver.solve_with_theory(&mut theory), SolverResult::Sat);
2609 assert!(solver.model_value(Var::new(1)).is_true()); }
2611
2612 #[test]
2613 fn test_solve_with_theory_unsat() {
2614 let mut solver = Solver::new();
2615 let mut theory = NullTheory;
2616
2617 let _x = solver.new_var();
2618
2619 solver.add_clause_dimacs(&[1]);
2621 solver.add_clause_dimacs(&[-1]);
2623
2624 assert_eq!(solver.solve_with_theory(&mut theory), SolverResult::Unsat);
2625 }
2626
2627 struct ImplicationTheory {
2629 x0_true: bool,
2631 }
2632
2633 impl ImplicationTheory {
2634 fn new() -> Self {
2635 Self { x0_true: false }
2636 }
2637 }
2638
2639 impl TheoryCallback for ImplicationTheory {
2640 fn on_assignment(&mut self, lit: Lit) -> TheoryCheckResult {
2641 if lit.var().index() == 0 && lit.is_pos() {
2643 self.x0_true = true;
2644 let reason: SmallVec<[Lit; 8]> = smallvec::smallvec![Lit::pos(Var::new(0))];
2647 return TheoryCheckResult::Propagated(vec![(Lit::pos(Var::new(1)), reason)]);
2648 }
2649 TheoryCheckResult::Sat
2650 }
2651
2652 fn final_check(&mut self) -> TheoryCheckResult {
2653 TheoryCheckResult::Sat
2654 }
2655
2656 fn on_backtrack(&mut self, _level: u32) {
2657 self.x0_true = false;
2658 }
2659 }
2660
2661 #[test]
2662 fn test_theory_propagation() {
2663 let mut solver = Solver::new();
2664 let mut theory = ImplicationTheory::new();
2665
2666 let _x0 = solver.new_var();
2667 let _x1 = solver.new_var();
2668
2669 solver.add_clause_dimacs(&[1]);
2671
2672 let result = solver.solve_with_theory(&mut theory);
2673 assert_eq!(result, SolverResult::Sat);
2674
2675 assert!(solver.model_value(Var::new(0)).is_true());
2677 assert!(solver.model_value(Var::new(1)).is_true());
2679 }
2680
2681 struct MutexTheory {
2683 x0_true: Option<Lit>,
2684 x1_true: Option<Lit>,
2685 }
2686
2687 impl MutexTheory {
2688 fn new() -> Self {
2689 Self {
2690 x0_true: None,
2691 x1_true: None,
2692 }
2693 }
2694 }
2695
2696 impl TheoryCallback for MutexTheory {
2697 fn on_assignment(&mut self, lit: Lit) -> TheoryCheckResult {
2698 if lit.var().index() == 0 && lit.is_pos() {
2699 self.x0_true = Some(lit);
2700 }
2701 if lit.var().index() == 1 && lit.is_pos() {
2702 self.x1_true = Some(lit);
2703 }
2704
2705 if self.x0_true.is_some() && self.x1_true.is_some() {
2707 let conflict: SmallVec<[Lit; 8]> = smallvec::smallvec![
2709 Lit::pos(Var::new(0)), Lit::pos(Var::new(1)) ];
2712 return TheoryCheckResult::Conflict(conflict);
2713 }
2714 TheoryCheckResult::Sat
2715 }
2716
2717 fn final_check(&mut self) -> TheoryCheckResult {
2718 if self.x0_true.is_some() && self.x1_true.is_some() {
2719 let conflict: SmallVec<[Lit; 8]> =
2720 smallvec::smallvec![Lit::pos(Var::new(0)), Lit::pos(Var::new(1))];
2721 return TheoryCheckResult::Conflict(conflict);
2722 }
2723 TheoryCheckResult::Sat
2724 }
2725
2726 fn on_backtrack(&mut self, _level: u32) {
2727 self.x0_true = None;
2728 self.x1_true = None;
2729 }
2730 }
2731
2732 #[test]
2733 fn test_theory_conflict() {
2734 let mut solver = Solver::new();
2735 let mut theory = MutexTheory::new();
2736
2737 let _x0 = solver.new_var();
2738 let _x1 = solver.new_var();
2739
2740 solver.add_clause_dimacs(&[1]);
2742 solver.add_clause_dimacs(&[2]);
2743
2744 let result = solver.solve_with_theory(&mut theory);
2745 assert_eq!(result, SolverResult::Unsat);
2746 }
2747
2748 #[test]
2749 fn test_solve_with_assumptions_sat() {
2750 let mut solver = Solver::new();
2751
2752 let x0 = solver.new_var();
2753 let x1 = solver.new_var();
2754
2755 solver.add_clause([Lit::pos(x0), Lit::pos(x1)]);
2757
2758 let assumptions = [Lit::pos(x0)];
2760 let (result, core) = solver.solve_with_assumptions(&assumptions);
2761
2762 assert_eq!(result, SolverResult::Sat);
2763 assert!(core.is_none());
2764 }
2765
2766 #[test]
2767 fn test_solve_with_assumptions_unsat() {
2768 let mut solver = Solver::new();
2769
2770 let x0 = solver.new_var();
2771 let x1 = solver.new_var();
2772
2773 solver.add_clause([Lit::neg(x0), Lit::neg(x1)]);
2775
2776 let assumptions = [Lit::pos(x0), Lit::pos(x1)];
2778 let (result, core) = solver.solve_with_assumptions(&assumptions);
2779
2780 assert_eq!(result, SolverResult::Unsat);
2781 assert!(core.is_some());
2782 let core = core.unwrap();
2783 assert!(!core.is_empty());
2785 }
2786
2787 #[test]
2788 fn test_solve_with_assumptions_core_extraction() {
2789 let mut solver = Solver::new();
2790
2791 let x0 = solver.new_var();
2792 let x1 = solver.new_var();
2793 let x2 = solver.new_var();
2794
2795 solver.add_clause([Lit::neg(x0)]);
2797
2798 let assumptions = [Lit::pos(x0), Lit::pos(x1), Lit::pos(x2)];
2801 let (result, core) = solver.solve_with_assumptions(&assumptions);
2802
2803 assert_eq!(result, SolverResult::Unsat);
2804 assert!(core.is_some());
2805 let core = core.unwrap();
2806 assert!(core.contains(&Lit::pos(x0)));
2808 }
2809
2810 #[test]
2811 fn test_solve_with_assumptions_incremental() {
2812 let mut solver = Solver::new();
2813
2814 let x0 = solver.new_var();
2815 let x1 = solver.new_var();
2816
2817 solver.add_clause([Lit::pos(x0), Lit::pos(x1)]);
2819
2820 let (result1, _) = solver.solve_with_assumptions(&[Lit::neg(x0)]);
2822 assert_eq!(result1, SolverResult::Sat);
2823
2824 let (result2, core2) = solver.solve_with_assumptions(&[Lit::neg(x0), Lit::neg(x1)]);
2826 assert_eq!(result2, SolverResult::Unsat);
2827 assert!(core2.is_some());
2828
2829 let (result3, _) = solver.solve_with_assumptions(&[Lit::pos(x0)]);
2831 assert_eq!(result3, SolverResult::Sat);
2832 }
2833
2834 #[test]
2835 fn test_push_pop_simple() {
2836 let mut solver = Solver::new();
2837
2838 let x0 = solver.new_var();
2839
2840 assert_eq!(solver.solve(), SolverResult::Sat);
2842
2843 solver.push();
2845 solver.add_clause([Lit::pos(x0)]);
2846 assert_eq!(solver.solve(), SolverResult::Sat);
2847 assert!(solver.model_value(x0).is_true());
2848
2849 solver.pop();
2851 let result = solver.solve();
2852 assert_eq!(
2853 result,
2854 SolverResult::Sat,
2855 "After pop, expected SAT but got {:?}. trivially_unsat={}",
2856 result,
2857 solver.trivially_unsat
2858 );
2859 }
2860
2861 #[test]
2862 fn test_push_pop_incremental() {
2863 let mut solver = Solver::new();
2864
2865 let x0 = solver.new_var();
2866 let x1 = solver.new_var();
2867 let x2 = solver.new_var();
2868
2869 solver.add_clause([Lit::pos(x0), Lit::pos(x1)]);
2871 assert_eq!(solver.solve(), SolverResult::Sat);
2872
2873 solver.push();
2875 solver.add_clause([Lit::neg(x0)]);
2876 assert_eq!(solver.solve(), SolverResult::Sat);
2877 assert!(solver.model_value(x1).is_true());
2879
2880 solver.push();
2882 solver.add_clause([Lit::neg(x1)]);
2883 assert_eq!(solver.solve(), SolverResult::Unsat);
2884
2885 solver.pop();
2887 assert_eq!(solver.solve(), SolverResult::Sat);
2888 assert!(solver.model_value(x1).is_true());
2889
2890 solver.pop();
2892 assert_eq!(solver.solve(), SolverResult::Sat);
2893 solver.push();
2897 solver.add_clause([Lit::pos(x0)]);
2898 solver.add_clause([Lit::pos(x2)]);
2899 assert_eq!(solver.solve(), SolverResult::Sat);
2900 assert!(solver.model_value(x0).is_true());
2901 assert!(solver.model_value(x2).is_true());
2902
2903 solver.pop();
2905 assert_eq!(solver.solve(), SolverResult::Sat);
2906 }
2907
2908 #[test]
2909 fn test_push_pop_with_learned_clauses() {
2910 let mut solver = Solver::new();
2911
2912 let x0 = solver.new_var();
2913 let x1 = solver.new_var();
2914 let x2 = solver.new_var();
2915
2916 solver.add_clause([Lit::pos(x0), Lit::pos(x1)]);
2919 solver.add_clause([Lit::neg(x0), Lit::pos(x2)]);
2920 solver.add_clause([Lit::neg(x1), Lit::pos(x2)]);
2921
2922 assert_eq!(solver.solve(), SolverResult::Sat);
2923
2924 solver.push();
2926 solver.add_clause([Lit::neg(x2)]);
2927
2928 assert_eq!(solver.solve(), SolverResult::Unsat);
2930
2931 solver.pop();
2933
2934 assert_eq!(solver.solve(), SolverResult::Sat);
2936 }
2937}