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];
467
468 if self.trail.lit_value(lit).is_false() {
469 let var = lit.var();
473 let level = self.trail.level(var);
474 if level == 0 {
475 self.trivially_unsat = true;
477 return false;
478 } else {
479 self.backtrack_to_root();
482 self.trail.assign_decision(lit);
483 return true;
484 }
485 }
486
487 if self.trail.lit_value(lit).is_true() {
488 let var = lit.var();
490 let level = self.trail.level(var);
491 if level == 0 {
492 return true;
494 }
495 self.backtrack_to_root();
497 self.trail.assign_decision(lit);
498 return true;
499 }
500
501 if self.trail.decision_level() > 0 {
504 self.backtrack_to_root();
505 }
506 self.trail.assign_decision(lit);
507 return true;
508 }
509 2 => {
510 let lit0 = clause_lits[0];
512 let lit1 = clause_lits[1];
513 let val0 = self.trail.lit_value(lit0);
514 let val1 = self.trail.lit_value(lit1);
515
516 if val0.is_true() || val1.is_true() {
518 let clause_id = self.clauses.add_original(clause_lits.iter().copied());
520 if let Some(current_level_clauses) = self.assertion_clause_ids.last_mut() {
521 current_level_clauses.push(clause_id);
522 }
523 self.binary_graph.add(lit0.negate(), lit1, clause_id);
524 self.binary_graph.add(lit1.negate(), lit0, clause_id);
525 self.watches
526 .add(lit0.negate(), Watcher::new(clause_id, lit1));
527 self.watches
528 .add(lit1.negate(), Watcher::new(clause_id, lit0));
529 return true;
530 }
531
532 if val0.is_false() && val1.is_false() {
534 let level0 = self.trail.level(lit0.var());
536 let level1 = self.trail.level(lit1.var());
537
538 if level0 == 0 && level1 == 0 {
539 self.trivially_unsat = true;
541 return false;
542 }
543
544 self.backtrack_to_root();
547 }
548
549 let clause_id = self.clauses.add_original(clause_lits.iter().copied());
553 if let Some(current_level_clauses) = self.assertion_clause_ids.last_mut() {
554 current_level_clauses.push(clause_id);
555 }
556 self.binary_graph.add(lit0.negate(), lit1, clause_id);
557 self.binary_graph.add(lit1.negate(), lit0, clause_id);
558 self.watches
559 .add(lit0.negate(), Watcher::new(clause_id, lit1));
560 self.watches
561 .add(lit1.negate(), Watcher::new(clause_id, lit0));
562 return true;
563 }
564 _ => {}
565 }
566
567 let num_false = clause_lits
570 .iter()
571 .filter(|&l| self.trail.lit_value(*l).is_false())
572 .count();
573 let has_true = clause_lits
574 .iter()
575 .any(|l| self.trail.lit_value(*l).is_true());
576
577 if !has_true && num_false == clause_lits.len() {
578 let all_at_zero = clause_lits.iter().all(|l| self.trail.level(l.var()) == 0);
581 if all_at_zero {
582 self.trivially_unsat = true;
583 return false;
584 }
585 self.backtrack_to_root();
587 }
588
589 let clause_id = self.clauses.add_original(clause_lits.iter().copied());
590
591 if let Some(current_level_clauses) = self.assertion_clause_ids.last_mut() {
593 current_level_clauses.push(clause_id);
594 }
595
596 let lit0 = clause_lits[0];
598 let lit1 = clause_lits[1];
599
600 self.watches
601 .add(lit0.negate(), Watcher::new(clause_id, lit1));
602 self.watches
603 .add(lit1.negate(), Watcher::new(clause_id, lit0));
604
605 true
606 }
607
608 pub fn add_clause_dimacs(&mut self, lits: &[i32]) -> bool {
610 self.add_clause(lits.iter().map(|&l| Lit::from_dimacs(l)))
611 }
612
613 pub fn solve(&mut self) -> SolverResult {
615 if self.trivially_unsat {
617 return SolverResult::Unsat;
618 }
619
620 if self.propagate().is_some() {
622 return SolverResult::Unsat;
623 }
624
625 loop {
626 if let Some(conflict) = self.propagate() {
628 self.stats.conflicts += 1;
629 self.conflicts_since_inprocessing += 1;
630
631 if self.trail.decision_level() == 0 {
632 return SolverResult::Unsat;
633 }
634
635 let (backtrack_level, learnt_clause) = self.analyze(conflict);
637
638 self.backtrack_with_phase_saving(backtrack_level);
640
641 if learnt_clause.len() == 1 {
643 let clause_id = self.clauses.add_learned(learnt_clause.iter().copied());
645 self.stats.learned_clauses += 1;
646 self.stats.unit_clauses += 1;
647 self.learned_clause_ids.push(clause_id);
648
649 if let Some(current_level_clauses) = self.assertion_clause_ids.last_mut() {
651 current_level_clauses.push(clause_id);
652 }
653
654 self.trail.assign_decision(learnt_clause[0]);
655 } else {
656 let lbd = self.compute_lbd(&learnt_clause);
658
659 self.recent_lbd_sum += u64::from(lbd);
661 self.recent_lbd_count += 1;
662 self.global_lbd_sum += u64::from(lbd);
663 self.global_lbd_count += 1;
664
665 if self.recent_lbd_count >= 5000 {
667 self.recent_lbd_sum /= 2;
668 self.recent_lbd_count /= 2;
669 }
670
671 let clause_id = self.clauses.add_learned(learnt_clause.iter().copied());
672 self.stats.learned_clauses += 1;
673
674 if let Some(clause) = self.clauses.get_mut(clause_id) {
676 clause.lbd = lbd;
677 }
678
679 self.learned_clause_ids.push(clause_id);
681
682 if let Some(current_level_clauses) = self.assertion_clause_ids.last_mut() {
684 current_level_clauses.push(clause_id);
685 }
686
687 let lit0 = learnt_clause[0];
689 let lit1 = learnt_clause[1];
690 self.watches
691 .add(lit0.negate(), Watcher::new(clause_id, lit1));
692 self.watches
693 .add(lit1.negate(), Watcher::new(clause_id, lit0));
694
695 self.trail.assign_propagation(learnt_clause[0], clause_id);
697 }
698
699 self.vsids.decay();
701 self.chb.decay();
702 self.lrb.decay();
703 self.lrb.on_conflict();
704 self.clauses.decay_activity(self.config.clause_decay);
705 self.clause_bump_increment /= self.config.clause_decay;
707
708 self.conflicts_since_deletion += 1;
710
711 if self.conflicts_since_deletion >= self.config.clause_deletion_threshold as u64 {
713 self.reduce_clause_database();
714 self.conflicts_since_deletion = 0;
715
716 if self.stats.restarts.is_multiple_of(10) {
718 let saved_level = self.trail.decision_level();
719 if saved_level == 0 {
720 self.vivify_clauses();
721 }
722 }
723 }
724
725 if self.stats.conflicts >= self.restart_threshold {
727 self.restart();
728 }
729
730 if self.config.enable_inprocessing
732 && self.conflicts_since_inprocessing >= self.config.inprocessing_interval
733 {
734 self.inprocess();
735 self.conflicts_since_inprocessing = 0;
736 }
737 } else {
738 if let Some(var) = self.pick_branch_var() {
740 self.stats.decisions += 1;
741 self.trail.new_decision_level();
742
743 let polarity = if self.rand_bool(self.config.random_polarity_prob) {
745 self.rand_bool(0.5)
747 } else {
748 self.phase[var.index()]
750 };
751 let lit = if polarity {
752 Lit::pos(var)
753 } else {
754 Lit::neg(var)
755 };
756 self.trail.assign_decision(lit);
757 } else {
758 self.save_model();
760 return SolverResult::Sat;
761 }
762 }
763 }
764 }
765
766 pub fn solve_with_assumptions(
777 &mut self,
778 assumptions: &[Lit],
779 ) -> (SolverResult, Option<Vec<Lit>>) {
780 if self.trivially_unsat {
781 return (SolverResult::Unsat, Some(Vec::new()));
782 }
783
784 for &lit in assumptions {
786 while self.num_vars <= lit.var().index() {
787 self.new_var();
788 }
789 }
790
791 if self.propagate().is_some() {
793 return (SolverResult::Unsat, Some(Vec::new()));
794 }
795
796 let assumption_level_start = self.trail.decision_level();
798
799 for (i, &lit) in assumptions.iter().enumerate() {
801 let value = self.trail.lit_value(lit);
803 if value.is_true() {
804 continue; }
806 if value.is_false() {
807 let core = self.extract_assumption_core(assumptions, i);
809 self.backtrack(assumption_level_start);
810 return (SolverResult::Unsat, Some(core));
811 }
812
813 self.trail.new_decision_level();
815 self.trail.assign_decision(lit);
816
817 if let Some(_conflict) = self.propagate() {
819 let core = self.analyze_assumption_conflict(assumptions);
821 self.backtrack(assumption_level_start);
822 return (SolverResult::Unsat, Some(core));
823 }
824 }
825
826 loop {
828 if let Some(conflict) = self.propagate() {
829 self.stats.conflicts += 1;
830
831 let backtrack_level = self.analyze_conflict_level(conflict);
833
834 if backtrack_level <= assumption_level_start {
835 let core = self.analyze_assumption_conflict(assumptions);
837 self.backtrack(assumption_level_start);
838 return (SolverResult::Unsat, Some(core));
839 }
840
841 let (bt_level, learnt_clause) = self.analyze(conflict);
842 self.backtrack_with_phase_saving(bt_level.max(assumption_level_start + 1));
843 self.learn_clause(learnt_clause);
844
845 self.vsids.decay();
846 self.clauses.decay_activity(self.config.clause_decay);
847 self.handle_clause_deletion_and_restart_limited(assumption_level_start);
848 } else {
849 if let Some(var) = self.pick_branch_var() {
851 self.stats.decisions += 1;
852 self.trail.new_decision_level();
853
854 let polarity = if self.rand_bool(self.config.random_polarity_prob) {
855 self.rand_bool(0.5)
856 } else {
857 self.phase.get(var.index()).copied().unwrap_or(false)
858 };
859 let lit = if polarity {
860 Lit::pos(var)
861 } else {
862 Lit::neg(var)
863 };
864 self.trail.assign_decision(lit);
865 } else {
866 self.save_model();
868 self.backtrack(assumption_level_start);
869 return (SolverResult::Sat, None);
870 }
871 }
872 }
873 }
874
875 fn extract_assumption_core(&self, assumptions: &[Lit], conflict_idx: usize) -> Vec<Lit> {
877 let mut core = Vec::new();
879 let conflict_lit = assumptions[conflict_idx];
880
881 for &lit in &assumptions[..=conflict_idx] {
883 if self.seen.get(lit.var().index()).copied().unwrap_or(false) || lit == conflict_lit {
884 core.push(lit);
885 }
886 }
887
888 if core.is_empty() {
890 core.push(conflict_lit);
891 }
892
893 core
894 }
895
896 fn analyze_assumption_conflict(&mut self, assumptions: &[Lit]) -> Vec<Lit> {
898 let mut core = Vec::new();
900
901 for &lit in assumptions {
903 let var = lit.var();
904 if var.index() < self.trail.assignments().len() {
905 let value = self.trail.lit_value(lit);
906 if value.is_false() || self.seen.get(var.index()).copied().unwrap_or(false) {
908 core.push(lit);
909 }
910 }
911 }
912
913 if core.is_empty() {
915 core.extend(assumptions.iter().copied());
916 }
917
918 core
919 }
920
921 fn analyze_conflict_level(&self, conflict: ClauseId) -> u32 {
923 let clause = match self.clauses.get(conflict) {
924 Some(c) => c,
925 None => return 0,
926 };
927
928 let mut min_level = u32::MAX;
929 for lit in clause.lits.iter().copied() {
930 let level = self.trail.level(lit.var());
931 if level > 0 && level < min_level {
932 min_level = level;
933 }
934 }
935
936 if min_level == u32::MAX { 0 } else { min_level }
937 }
938
939 fn handle_clause_deletion_and_restart_limited(&mut self, min_level: u32) {
941 self.conflicts_since_deletion += 1;
942
943 if self.conflicts_since_deletion >= self.config.clause_deletion_threshold as u64 {
944 self.reduce_clause_database();
945 self.conflicts_since_deletion = 0;
946 }
947
948 if self.stats.conflicts >= self.restart_threshold {
949 self.backtrack(min_level);
951 self.stats.restarts += 1;
952 self.luby_index += 1;
953 self.restart_threshold =
954 self.stats.conflicts + self.config.restart_interval * Self::luby(self.luby_index);
955 }
956 }
957
958 fn propagate(&mut self) -> Option<ClauseId> {
960 while let Some(lit) = self.trail.next_to_propagate() {
961 self.stats.propagations += 1;
962
963 let binary_implications = self.binary_graph.get(lit).to_vec();
965 for &(implied_lit, clause_id) in &binary_implications {
966 let value = self.trail.lit_value(implied_lit);
967 if value.is_false() {
968 return Some(clause_id);
970 } else if !value.is_defined() {
971 self.trail.assign_propagation(implied_lit, clause_id);
973
974 if self.config.enable_lazy_hyper_binary {
976 self.check_hyper_binary_resolution(lit, implied_lit, clause_id);
977 }
978 }
979 }
980
981 let watches = std::mem::take(self.watches.get_mut(lit));
983
984 let mut i = 0;
985 while i < watches.len() {
986 let watcher = watches[i];
987
988 if self.trail.lit_value(watcher.blocker).is_true() {
990 i += 1;
991 continue;
992 }
993
994 let clause = match self.clauses.get_mut(watcher.clause) {
995 Some(c) if !c.deleted => c,
996 _ => {
997 i += 1;
998 continue;
999 }
1000 };
1001
1002 if clause.lits[0] == lit.negate() {
1004 clause.swap(0, 1);
1005 }
1006
1007 let first = clause.lits[0];
1009 if self.trail.lit_value(first).is_true() {
1010 self.watches
1012 .get_mut(lit)
1013 .push(Watcher::new(watcher.clause, first));
1014 i += 1;
1015 continue;
1016 }
1017
1018 let mut found = false;
1020 for j in 2..clause.lits.len() {
1021 let l = clause.lits[j];
1022 if !self.trail.lit_value(l).is_false() {
1023 clause.swap(1, j);
1024 self.watches
1025 .add(clause.lits[1].negate(), Watcher::new(watcher.clause, first));
1026 found = true;
1027 break;
1028 }
1029 }
1030
1031 if found {
1032 i += 1;
1033 continue;
1034 }
1035
1036 self.watches
1038 .get_mut(lit)
1039 .push(Watcher::new(watcher.clause, first));
1040
1041 if self.trail.lit_value(first).is_false() {
1042 for j in (i + 1)..watches.len() {
1045 self.watches.get_mut(lit).push(watches[j]);
1046 }
1047 return Some(watcher.clause);
1048 } else {
1049 self.trail.assign_propagation(first, watcher.clause);
1051
1052 if self.config.enable_lazy_hyper_binary {
1054 self.check_hyper_binary_resolution(lit, first, watcher.clause);
1055 }
1056 }
1057
1058 i += 1;
1059 }
1060 }
1061
1062 None
1063 }
1064
1065 fn analyze(&mut self, conflict: ClauseId) -> (u32, SmallVec<[Lit; 16]>) {
1067 if cfg!(debug_assertions) && self.num_vars <= 5 {
1069 eprintln!("[ANALYZE] Conflict clause id={:?}", conflict);
1070 if let Some(c) = self.clauses.get(conflict) {
1071 let lits_str: Vec<String> = c
1072 .lits
1073 .iter()
1074 .map(|lit| {
1075 let val = self.trail.lit_value(*lit);
1076 let level = self.trail.level(lit.var());
1077 let sign = if lit.is_pos() { "" } else { "~" };
1078 format!("{}v{}@{}={:?}", sign, lit.var().index(), level, val)
1079 })
1080 .collect();
1081 eprintln!("[ANALYZE] Conflict clause: ({})", lits_str.join(" | "));
1082 }
1083 eprintln!("[ANALYZE] Trail:");
1084 for &lit in self.trail.assignments() {
1085 let var = lit.var();
1086 let level = self.trail.level(var);
1087 let reason = self.trail.reason(var);
1088 let sign = if lit.is_pos() { "" } else { "~" };
1089 eprintln!(" {}v{}@{} reason={:?}", sign, var.index(), level, reason);
1090 }
1091 }
1092
1093 self.learnt.clear();
1094 self.learnt.push(Lit::from_code(0)); let mut counter = 0;
1097 let mut p = None;
1098 let mut index = self.trail.assignments().len();
1099 let current_level = self.trail.decision_level();
1100
1101 for s in &mut self.seen {
1103 *s = false;
1104 }
1105
1106 let mut reason_clause = conflict;
1107
1108 loop {
1109 let Some(clause) = self.clauses.get(reason_clause) else {
1111 break; };
1113 let start = if p.is_some() { 1 } else { 0 };
1114 let is_learned = clause.learned;
1115
1116 if is_learned && let Some(clause_mut) = self.clauses.get_mut(reason_clause) {
1118 clause_mut.record_usage();
1119 if clause_mut.lbd <= 2 {
1121 clause_mut.promote_to_core();
1122 }
1123 clause_mut.activity += self.clause_bump_increment;
1125 }
1126
1127 let Some(clause) = self.clauses.get(reason_clause) else {
1128 break;
1129 };
1130 for &lit in &clause.lits[start..] {
1131 let var = lit.var();
1132 let level = self.trail.level(var);
1133
1134 if !self.seen[var.index()] && level > 0 {
1135 self.seen[var.index()] = true;
1136 self.vsids.bump(var);
1137 self.chb.bump(var);
1138 self.lrb.on_reason(var);
1139
1140 if level == current_level {
1141 counter += 1;
1142 } else {
1143 self.learnt.push(lit);
1147 }
1148 }
1149 }
1150
1151 let mut current_lit;
1153 loop {
1154 index -= 1;
1155 current_lit = self.trail.assignments()[index];
1156 p = Some(current_lit);
1157 if self.seen[current_lit.var().index()] {
1158 break;
1159 }
1160 }
1161
1162 counter -= 1;
1163 if counter == 0 {
1164 break;
1165 }
1166
1167 let var = current_lit.var();
1168 match self.trail.reason(var) {
1169 Reason::Propagation(c) => reason_clause = c,
1170 _ => break,
1171 }
1172 }
1173
1174 if let Some(lit) = p {
1176 self.learnt[0] = lit.negate();
1177 }
1178
1179 self.minimize_learnt_clause();
1181
1182 let assertion_level = if self.learnt.len() == 1 {
1184 0
1185 } else {
1186 let mut max_level = 0;
1188 let mut max_idx = 1;
1189 for (i, &lit) in self.learnt.iter().enumerate().skip(1) {
1190 let level = self.trail.level(lit.var());
1191 if level > max_level {
1192 max_level = level;
1193 max_idx = i;
1194 }
1195 }
1196 self.learnt.swap(1, max_idx);
1198 max_level
1199 };
1200
1201 let backtrack_level = self.chrono_backtrack.compute_backtrack_level(
1203 &self.trail,
1204 &self.learnt,
1205 assertion_level,
1206 );
1207
1208 if backtrack_level != assertion_level {
1210 self.stats.chrono_backtracks += 1;
1211 } else {
1212 self.stats.non_chrono_backtracks += 1;
1213 }
1214
1215 if cfg!(debug_assertions) && self.num_vars <= 5 {
1217 let lits_str: Vec<String> = self
1218 .learnt
1219 .iter()
1220 .map(|lit| {
1221 let sign = if lit.is_pos() { "" } else { "~" };
1222 format!("{}v{}", sign, lit.var().index())
1223 })
1224 .collect();
1225 eprintln!(
1226 "[ANALYZE] Learned clause: ({}), backtrack_level={}",
1227 lits_str.join(" | "),
1228 backtrack_level
1229 );
1230 }
1231
1232 (backtrack_level, self.learnt.clone())
1233 }
1234
1235 fn pick_branch_var(&mut self) -> Option<Var> {
1237 if self.config.use_lrb_branching {
1238 while let Some(var) = self.lrb.select() {
1240 if !self.trail.is_assigned(var) {
1241 self.lrb.on_assign(var);
1242 return Some(var);
1243 }
1244 }
1245 } else if self.config.use_chb_branching {
1246 if self.stats.decisions.is_multiple_of(100) {
1249 self.chb.rebuild_heap();
1250 }
1251
1252 while let Some(var) = self.chb.pop_max() {
1253 if !self.trail.is_assigned(var) {
1254 return Some(var);
1255 }
1256 }
1257 } else {
1258 while let Some(var) = self.vsids.pop_max() {
1260 if !self.trail.is_assigned(var) {
1261 return Some(var);
1262 }
1263 }
1264 }
1265 None
1266 }
1267
1268 fn minimize_learnt_clause(&mut self) {
1279 if self.learnt.len() <= 2 {
1280 return;
1282 }
1283
1284 let original_len = self.learnt.len();
1285
1286 self.analyze_stack.clear();
1289
1290 let mut j = 1; for i in 1..self.learnt.len() {
1293 let lit = self.learnt[i];
1294 if self.lit_is_redundant(lit) {
1295 } else {
1297 self.learnt[j] = lit;
1299 j += 1;
1300 }
1301 }
1302 self.learnt.truncate(j);
1303
1304 self.strengthen_learnt_clause();
1308
1309 let final_len = self.learnt.len();
1311 if final_len < original_len {
1312 self.stats.minimizations += 1;
1313 self.stats.literals_removed += (original_len - final_len) as u64;
1314 }
1315 }
1316
1317 fn strengthen_learnt_clause(&mut self) {
1319 if self.learnt.len() <= 2 {
1320 return;
1321 }
1322
1323 let mut j = 1;
1325 for i in 1..self.learnt.len() {
1326 let lit = self.learnt[i];
1327 let var = lit.var();
1328
1329 if let Reason::Propagation(reason_id) = self.trail.reason(var)
1331 && let Some(reason_clause) = self.clauses.get(reason_id)
1332 && reason_clause.lits.len() == 2
1333 {
1334 let other_lit = if reason_clause.lits[0] == lit.negate() {
1336 reason_clause.lits[1]
1337 } else if reason_clause.lits[1] == lit.negate() {
1338 reason_clause.lits[0]
1339 } else {
1340 self.learnt[j] = lit;
1342 j += 1;
1343 continue;
1344 };
1345
1346 if self.trail.level(other_lit.var()) == 0 && self.seen[other_lit.var().index()] {
1349 continue;
1351 }
1352 }
1353
1354 self.learnt[j] = lit;
1356 j += 1;
1357 }
1358 self.learnt.truncate(j);
1359 }
1360
1361 fn lit_is_redundant(&mut self, lit: Lit) -> bool {
1368 let var = lit.var();
1369
1370 let reason = match self.trail.reason(var) {
1372 Reason::Decision => return false,
1373 Reason::Theory => return false, Reason::Propagation(c) => c,
1375 };
1376
1377 let reason_clause = match self.clauses.get(reason) {
1378 Some(c) => c,
1379 None => return false,
1380 };
1381
1382 for &reason_lit in &reason_clause.lits {
1384 if reason_lit == lit.negate() {
1385 continue;
1387 }
1388
1389 let reason_var = reason_lit.var();
1390
1391 if self.trail.level(reason_var) == 0 {
1393 continue;
1394 }
1395
1396 if self.seen[reason_var.index()] {
1398 continue;
1399 }
1400
1401 return false;
1404 }
1405
1406 true
1407 }
1408
1409 fn backtrack_with_phase_saving(&mut self, level: u32) {
1411 let mut unassigned_vars = Vec::new();
1413
1414 let phase = &mut self.phase;
1416 let lrb = &mut self.lrb;
1417 self.trail.backtrack_to_with_callback(level, |lit| {
1418 let var = lit.var();
1419 if var.index() < phase.len() {
1420 phase[var.index()] = lit.is_pos();
1421 }
1422 lrb.unassign(var);
1424 unassigned_vars.push(var);
1425 });
1426
1427 for var in unassigned_vars {
1429 if !self.vsids.contains(var) {
1430 self.vsids.insert(var);
1431 }
1432 if !self.chb.contains(var) {
1433 self.chb.insert(var);
1434 }
1435 }
1436 }
1437
1438 fn backtrack(&mut self, level: u32) {
1440 self.trail.backtrack_to(level);
1441 }
1442
1443 fn luby(i: u64) -> u64 {
1447 let i = i + 1; let mut k = 1u32;
1451 while (1u64 << k) - 1 < i {
1452 k += 1;
1453 }
1454
1455 let seq_len = (1u64 << k) - 1;
1456
1457 if i == seq_len {
1458 1u64 << (k - 1)
1460 } else {
1461 let half_len = (1u64 << (k - 1)) - 1;
1464 if i <= half_len {
1465 Self::luby(i - 1) } else if i <= 2 * half_len {
1467 Self::luby(i - half_len - 1)
1468 } else {
1469 1u64 << (k - 1)
1470 }
1471 }
1472 }
1473
1474 fn restart(&mut self) {
1476 self.stats.restarts += 1;
1477 self.backtrack_with_phase_saving(0);
1478
1479 match self.config.restart_strategy {
1481 RestartStrategy::Luby => {
1482 self.luby_index += 1;
1483 self.restart_threshold = self.stats.conflicts
1484 + Self::luby(self.luby_index) * self.config.restart_interval;
1485 }
1486 RestartStrategy::Geometric => {
1487 let current_interval = if self.restart_threshold > self.stats.conflicts {
1488 self.restart_threshold - self.stats.conflicts
1489 } else {
1490 self.config.restart_interval
1491 };
1492 let next_interval =
1493 (current_interval as f64 * self.config.restart_multiplier) as u64;
1494 self.restart_threshold = self.stats.conflicts + next_interval;
1495 }
1496 RestartStrategy::Glucose => {
1497 let current_interval = if self.restart_threshold > self.stats.conflicts {
1501 self.restart_threshold - self.stats.conflicts
1502 } else {
1503 self.config.restart_interval
1504 };
1505
1506 let next_interval = if self.recent_lbd_count > 50 {
1508 let recent_avg = self.recent_lbd_sum / self.recent_lbd_count.max(1);
1509 if recent_avg < 5 {
1511 ((current_interval as f64) * 1.1) as u64
1513 } else {
1514 ((current_interval as f64) * 0.9) as u64
1516 }
1517 } else {
1518 current_interval
1519 };
1520
1521 self.restart_threshold = self.stats.conflicts + next_interval.max(100);
1522 }
1523 RestartStrategy::LocalLbd => {
1524 self.conflicts_since_local_restart += 1;
1527
1528 if self.conflicts_since_local_restart >= 50 && self.should_local_restart() {
1529 let local_level = self.compute_local_restart_level();
1531 self.backtrack_with_phase_saving(local_level);
1532 self.conflicts_since_local_restart = 0;
1533 self.recent_lbd_sum = 0;
1535 self.recent_lbd_count = 0;
1536 } else {
1537 let current_interval = if self.restart_threshold > self.stats.conflicts {
1539 self.restart_threshold - self.stats.conflicts
1540 } else {
1541 self.config.restart_interval
1542 };
1543 self.restart_threshold = self.stats.conflicts + current_interval;
1544 }
1545 return; }
1547 }
1548
1549 for i in 0..self.num_vars {
1551 let var = Var::new(i as u32);
1552 if !self.trail.is_assigned(var) && !self.vsids.contains(var) {
1553 self.vsids.insert(var);
1554 }
1555 }
1556 }
1557
1558 fn should_local_restart(&self) -> bool {
1561 if self.recent_lbd_count < 50 || self.global_lbd_count < 100 {
1562 return false;
1563 }
1564
1565 let recent_avg = self.recent_lbd_sum / self.recent_lbd_count.max(1);
1566 let global_avg = self.global_lbd_sum / self.global_lbd_count.max(1);
1567
1568 recent_avg * 4 > global_avg * 5
1570 }
1571
1572 fn compute_local_restart_level(&self) -> u32 {
1575 let current_level = self.trail.decision_level();
1576
1577 if current_level > 5 {
1579 current_level / 5
1580 } else {
1581 0
1582 }
1583 }
1584
1585 fn compute_lbd(&mut self, lits: &[Lit]) -> u32 {
1588 self.lbd_mark += 1;
1589 let mark = self.lbd_mark;
1590
1591 let mut count = 0u32;
1592 for &lit in lits {
1593 let level = self.trail.level(lit.var()) as usize;
1594 if level < self.level_marks.len() && self.level_marks[level] != mark {
1595 self.level_marks[level] = mark;
1596 count += 1;
1597 }
1598 }
1599
1600 count
1601 }
1602
1603 fn reduce_clause_database(&mut self) {
1608 use crate::clause::ClauseTier;
1609
1610 let mut core_candidates: Vec<(ClauseId, f64)> = Vec::new();
1611 let mut mid_candidates: Vec<(ClauseId, f64)> = Vec::new();
1612 let mut local_candidates: Vec<(ClauseId, f64)> = Vec::new();
1613
1614 for &cid in &self.learned_clause_ids {
1615 if let Some(clause) = self.clauses.get(cid) {
1616 if clause.deleted {
1617 continue;
1618 }
1619
1620 if clause.lits.len() <= 2 {
1622 continue;
1623 }
1624
1625 let is_reason = clause.lits.iter().any(|&lit| {
1628 let var = lit.var();
1629 if self.trail.is_assigned(var) {
1630 matches!(self.trail.reason(var), Reason::Propagation(r) if r == cid)
1631 } else {
1632 false
1633 }
1634 });
1635
1636 if !is_reason {
1637 match clause.tier {
1638 ClauseTier::Core => core_candidates.push((cid, clause.activity)),
1639 ClauseTier::Mid => mid_candidates.push((cid, clause.activity)),
1640 ClauseTier::Local => local_candidates.push((cid, clause.activity)),
1641 }
1642 }
1643 }
1644 }
1645
1646 core_candidates.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
1648 mid_candidates.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
1649 local_candidates.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
1650
1651 let num_core_delete = core_candidates.len() / 10;
1654 let num_mid_delete = (mid_candidates.len() * 3) / 10;
1656 let num_local_delete = (local_candidates.len() * 3) / 4;
1658
1659 for (cid, _) in core_candidates.iter().take(num_core_delete) {
1660 self.clauses.remove(*cid);
1661 self.stats.deleted_clauses += 1;
1662 }
1663
1664 for (cid, _) in mid_candidates.iter().take(num_mid_delete) {
1665 self.clauses.remove(*cid);
1666 self.stats.deleted_clauses += 1;
1667 }
1668
1669 for (cid, _) in local_candidates.iter().take(num_local_delete) {
1670 self.clauses.remove(*cid);
1671 self.stats.deleted_clauses += 1;
1672 }
1673
1674 self.learned_clause_ids
1676 .retain(|&cid| self.clauses.get(cid).is_some_and(|c| !c.deleted));
1677 }
1678
1679 fn save_model(&mut self) {
1681 self.model.resize(self.num_vars, LBool::Undef);
1682 for i in 0..self.num_vars {
1683 self.model[i] = self.trail.value(Var::new(i as u32));
1684 }
1685 }
1686
1687 #[must_use]
1689 pub fn model(&self) -> &[LBool] {
1690 &self.model
1691 }
1692
1693 #[must_use]
1695 pub fn model_value(&self, var: Var) -> LBool {
1696 self.model.get(var.index()).copied().unwrap_or(LBool::Undef)
1697 }
1698
1699 #[must_use]
1701 pub fn stats(&self) -> &SolverStats {
1702 &self.stats
1703 }
1704
1705 #[must_use]
1707 pub fn num_vars(&self) -> usize {
1708 self.num_vars
1709 }
1710
1711 #[must_use]
1713 pub fn num_clauses(&self) -> usize {
1714 self.clauses.len()
1715 }
1716
1717 pub fn push(&mut self) {
1723 self.backtrack_with_phase_saving(0);
1727
1728 self.assertion_levels.push(self.clauses.num_original());
1729 self.assertion_trail_sizes.push(self.trail.size());
1730 self.assertion_clause_ids.push(Vec::new());
1731 }
1732
1733 pub fn pop(&mut self) {
1735 if self.assertion_levels.len() > 1 {
1736 self.assertion_levels.pop();
1737
1738 let trail_size = self.assertion_trail_sizes.pop().unwrap_or(0);
1740
1741 if let Some(clause_ids_to_remove) = self.assertion_clause_ids.pop() {
1743 for clause_id in clause_ids_to_remove {
1744 self.clauses.remove(clause_id);
1746
1747 self.learned_clause_ids.retain(|&id| id != clause_id);
1749
1750 }
1753 }
1754
1755 let current_size = self.trail.size();
1760 if current_size > trail_size {
1761 let mut unassigned_vars = Vec::new();
1763 for i in trail_size..current_size {
1764 let lit = self.trail.assignments()[i];
1765 unassigned_vars.push(lit.var());
1766 }
1767
1768 self.trail.backtrack_to_size(trail_size);
1769
1770 for var in unassigned_vars {
1772 if !self.vsids.contains(var) {
1773 self.vsids.insert(var);
1774 }
1775 if !self.chb.contains(var) {
1776 self.chb.insert(var);
1777 }
1778 self.lrb.unassign(var);
1779 }
1780 }
1781
1782 self.backtrack_with_phase_saving(0);
1784
1785 self.trivially_unsat = false;
1787 }
1788 }
1789
1790 pub fn backtrack_to_root(&mut self) {
1797 self.backtrack_with_phase_saving(0);
1798 }
1799
1800 pub fn reset(&mut self) {
1802 self.clauses = ClauseDatabase::new();
1803 self.trail.clear();
1804 self.watches.clear();
1805 self.vsids.clear();
1806 self.chb.clear();
1807 self.stats = SolverStats::default();
1808 self.learnt.clear();
1809 self.seen.clear();
1810 self.analyze_stack.clear();
1811 self.assertion_levels.clear();
1812 self.assertion_levels.push(0);
1813 self.assertion_trail_sizes.clear();
1814 self.assertion_trail_sizes.push(0);
1815 self.assertion_clause_ids.clear();
1816 self.assertion_clause_ids.push(Vec::new());
1817 self.model.clear();
1818 self.num_vars = 0;
1819 self.restart_threshold = self.config.restart_interval;
1820 self.trivially_unsat = false;
1821 self.phase.clear();
1822 self.luby_index = 0;
1823 self.level_marks.clear();
1824 self.lbd_mark = 0;
1825 self.learned_clause_ids.clear();
1826 self.conflicts_since_deletion = 0;
1827 self.rng_state = 0x853c_49e6_748f_ea9b;
1828 self.recent_lbd_sum = 0;
1829 self.recent_lbd_count = 0;
1830 self.binary_graph.clear();
1831 self.global_lbd_sum = 0;
1832 self.global_lbd_count = 0;
1833 self.conflicts_since_local_restart = 0;
1834 }
1835
1836 pub fn solve_with_theory<T: TheoryCallback>(&mut self, theory: &mut T) -> SolverResult {
1845 if self.trivially_unsat {
1846 return SolverResult::Unsat;
1847 }
1848
1849 if self.propagate().is_some() {
1851 return SolverResult::Unsat;
1852 }
1853
1854 loop {
1855 if let Some(conflict) = self.propagate() {
1857 self.stats.conflicts += 1;
1858
1859 if self.trail.decision_level() == 0 {
1860 return SolverResult::Unsat;
1861 }
1862
1863 let (backtrack_level, learnt_clause) = self.analyze(conflict);
1864 theory.on_backtrack(backtrack_level);
1865 self.backtrack_with_phase_saving(backtrack_level);
1866 self.learn_clause(learnt_clause);
1867
1868 self.vsids.decay();
1869 self.clauses.decay_activity(self.config.clause_decay);
1870 self.handle_clause_deletion_and_restart();
1871 continue;
1872 }
1873
1874 loop {
1876 let assignments = self.trail.assignments().to_vec();
1878 let mut theory_conflict = None;
1879 let mut theory_propagations = Vec::new();
1880
1881 for &lit in &assignments {
1883 match theory.on_assignment(lit) {
1884 TheoryCheckResult::Sat => {}
1885 TheoryCheckResult::Conflict(conflict_lits) => {
1886 theory_conflict = Some(conflict_lits);
1887 break;
1888 }
1889 TheoryCheckResult::Propagated(props) => {
1890 theory_propagations.extend(props);
1891 }
1892 }
1893 }
1894
1895 if let Some(conflict_lits) = theory_conflict {
1897 self.stats.conflicts += 1;
1898
1899 if self.trail.decision_level() == 0 {
1900 return SolverResult::Unsat;
1901 }
1902
1903 let (backtrack_level, learnt_clause) =
1904 self.analyze_theory_conflict(&conflict_lits);
1905 theory.on_backtrack(backtrack_level);
1906 self.backtrack_with_phase_saving(backtrack_level);
1907 self.learn_clause(learnt_clause);
1908
1909 self.vsids.decay();
1910 self.clauses.decay_activity(self.config.clause_decay);
1911 self.handle_clause_deletion_and_restart();
1912 continue;
1913 }
1914
1915 let mut made_propagation = false;
1917 for (lit, reason_lits) in theory_propagations {
1918 if !self.trail.is_assigned(lit.var()) {
1919 let clause_id = self.add_theory_reason_clause(&reason_lits, lit);
1921 self.trail.assign_propagation(lit, clause_id);
1922 made_propagation = true;
1923 }
1924 }
1925
1926 if made_propagation {
1927 if let Some(conflict) = self.propagate() {
1929 self.stats.conflicts += 1;
1930
1931 if self.trail.decision_level() == 0 {
1932 return SolverResult::Unsat;
1933 }
1934
1935 let (backtrack_level, learnt_clause) = self.analyze(conflict);
1936 theory.on_backtrack(backtrack_level);
1937 self.backtrack_with_phase_saving(backtrack_level);
1938 self.learn_clause(learnt_clause);
1939
1940 self.vsids.decay();
1941 self.clauses.decay_activity(self.config.clause_decay);
1942 self.handle_clause_deletion_and_restart();
1943 }
1944 continue;
1945 }
1946
1947 break;
1948 }
1949
1950 if let Some(var) = self.pick_branch_var() {
1952 self.stats.decisions += 1;
1953 self.trail.new_decision_level();
1954 let new_level = self.trail.decision_level();
1955 theory.on_new_level(new_level);
1956
1957 let polarity = if self.rand_bool(self.config.random_polarity_prob) {
1958 self.rand_bool(0.5)
1959 } else {
1960 self.phase[var.index()]
1961 };
1962 let lit = if polarity {
1963 Lit::pos(var)
1964 } else {
1965 Lit::neg(var)
1966 };
1967 self.trail.assign_decision(lit);
1968 } else {
1969 match theory.final_check() {
1971 TheoryCheckResult::Sat => {
1972 self.save_model();
1973 return SolverResult::Sat;
1974 }
1975 TheoryCheckResult::Conflict(conflict_lits) => {
1976 self.stats.conflicts += 1;
1977
1978 if self.trail.decision_level() == 0 {
1979 return SolverResult::Unsat;
1980 }
1981
1982 let (backtrack_level, learnt_clause) =
1983 self.analyze_theory_conflict(&conflict_lits);
1984 theory.on_backtrack(backtrack_level);
1985 self.backtrack_with_phase_saving(backtrack_level);
1986 self.learn_clause(learnt_clause);
1987
1988 self.vsids.decay();
1989 self.clauses.decay_activity(self.config.clause_decay);
1990 self.handle_clause_deletion_and_restart();
1991 }
1992 TheoryCheckResult::Propagated(props) => {
1993 for (lit, reason_lits) in props {
1995 if !self.trail.is_assigned(lit.var()) {
1996 let clause_id = self.add_theory_reason_clause(&reason_lits, lit);
1997 self.trail.assign_propagation(lit, clause_id);
1998 }
1999 }
2000 }
2001 }
2002 }
2003 }
2004 }
2005
2006 fn analyze_theory_conflict(&mut self, conflict_lits: &[Lit]) -> (u32, SmallVec<[Lit; 16]>) {
2008 self.learnt.clear();
2009 self.learnt.push(Lit::from_code(0)); let mut counter = 0;
2012 let current_level = self.trail.decision_level();
2013
2014 for s in &mut self.seen {
2016 *s = false;
2017 }
2018
2019 for &lit in conflict_lits {
2021 let var = lit.var();
2022 let level = self.trail.level(var);
2023
2024 if !self.seen[var.index()] && level > 0 {
2025 self.seen[var.index()] = true;
2026 self.vsids.bump(var);
2027 self.chb.bump(var);
2028
2029 if level == current_level {
2030 counter += 1;
2031 } else {
2032 self.learnt.push(lit);
2037 }
2038 }
2039 }
2040
2041 let mut index = self.trail.assignments().len();
2043 let mut p = None;
2044
2045 while counter > 0 {
2046 index -= 1;
2047 let current_lit = self.trail.assignments()[index];
2048 p = Some(current_lit);
2049 let var = current_lit.var();
2050
2051 if self.seen[var.index()] {
2052 counter -= 1;
2053
2054 if counter > 0
2055 && let Reason::Propagation(reason_clause) = self.trail.reason(var)
2056 && let Some(clause) = self.clauses.get(reason_clause)
2057 {
2058 for &lit in &clause.lits[1..] {
2060 let reason_var = lit.var();
2061 let level = self.trail.level(reason_var);
2062
2063 if !self.seen[reason_var.index()] && level > 0 {
2064 self.seen[reason_var.index()] = true;
2065 self.vsids.bump(reason_var);
2066 self.chb.bump(reason_var);
2067
2068 if level == current_level {
2069 counter += 1;
2070 } else {
2071 self.learnt.push(lit);
2073 }
2074 }
2075 }
2076 }
2077 }
2078 }
2079
2080 if let Some(uip) = p {
2082 self.learnt[0] = uip.negate();
2083 }
2084
2085 self.minimize_learnt_clause();
2087
2088 let backtrack_level = if self.learnt.len() == 1 {
2090 0
2091 } else {
2092 let mut max_level = 0;
2093 let mut max_idx = 1;
2094 for (i, &lit) in self.learnt.iter().enumerate().skip(1) {
2095 let level = self.trail.level(lit.var());
2096 if level > max_level {
2097 max_level = level;
2098 max_idx = i;
2099 }
2100 }
2101 self.learnt.swap(1, max_idx);
2102 max_level
2103 };
2104
2105 (backtrack_level, self.learnt.clone())
2106 }
2107
2108 fn add_theory_reason_clause(&mut self, reason_lits: &[Lit], propagated_lit: Lit) -> ClauseId {
2111 let mut clause_lits: SmallVec<[Lit; 8]> = SmallVec::new();
2112 clause_lits.push(propagated_lit);
2113 for &lit in reason_lits {
2114 clause_lits.push(lit.negate());
2115 }
2116
2117 let clause_id = self.clauses.add_learned(clause_lits.iter().copied());
2118
2119 if clause_lits.len() >= 2 {
2121 let lit0 = clause_lits[0];
2122 let lit1 = clause_lits[1];
2123 self.watches
2124 .add(lit0.negate(), Watcher::new(clause_id, lit1));
2125 self.watches
2126 .add(lit1.negate(), Watcher::new(clause_id, lit0));
2127 }
2128
2129 clause_id
2130 }
2131
2132 fn learn_clause(&mut self, learnt_clause: SmallVec<[Lit; 16]>) {
2135 if learnt_clause.len() == 1 {
2136 let clause_id = self.clauses.add_learned(learnt_clause.iter().copied());
2138 self.stats.learned_clauses += 1;
2139 self.stats.unit_clauses += 1;
2140 self.learned_clause_ids.push(clause_id);
2141
2142 self.trail.assign_decision(learnt_clause[0]);
2143 } else if learnt_clause.len() == 2 {
2144 let lbd = self.compute_lbd(&learnt_clause);
2146 let clause_id = self.clauses.add_learned(learnt_clause.iter().copied());
2147 self.stats.learned_clauses += 1;
2148 self.stats.binary_clauses += 1;
2149 self.stats.total_lbd += lbd as u64;
2150
2151 if let Some(clause) = self.clauses.get_mut(clause_id) {
2152 clause.lbd = lbd;
2153 }
2154
2155 self.learned_clause_ids.push(clause_id);
2156
2157 let lit0 = learnt_clause[0];
2158 let lit1 = learnt_clause[1];
2159
2160 self.binary_graph.add(lit0.negate(), lit1, clause_id);
2162 self.binary_graph.add(lit1.negate(), lit0, clause_id);
2163
2164 self.watches
2165 .add(lit0.negate(), Watcher::new(clause_id, lit1));
2166 self.watches
2167 .add(lit1.negate(), Watcher::new(clause_id, lit0));
2168
2169 self.trail.assign_propagation(learnt_clause[0], clause_id);
2170 } else {
2171 let lbd = self.compute_lbd(&learnt_clause);
2172 self.stats.total_lbd += lbd as u64;
2173 let clause_id = self.clauses.add_learned(learnt_clause.iter().copied());
2174 self.stats.learned_clauses += 1;
2175
2176 if let Some(clause) = self.clauses.get_mut(clause_id) {
2177 clause.lbd = lbd;
2178 }
2179
2180 self.learned_clause_ids.push(clause_id);
2181
2182 let lit0 = learnt_clause[0];
2183 let lit1 = learnt_clause[1];
2184 self.watches
2185 .add(lit0.negate(), Watcher::new(clause_id, lit1));
2186 self.watches
2187 .add(lit1.negate(), Watcher::new(clause_id, lit0));
2188
2189 self.trail.assign_propagation(learnt_clause[0], clause_id);
2190
2191 if learnt_clause.len() <= 5 && lbd <= 3 {
2193 self.check_subsumption(clause_id);
2194 }
2195 }
2196 }
2197
2198 fn check_subsumption(&mut self, new_clause_id: ClauseId) {
2201 let new_clause = match self.clauses.get(new_clause_id) {
2202 Some(c) => c.lits.clone(),
2203 None => return,
2204 };
2205
2206 if new_clause.len() > 10 {
2207 return; }
2209
2210 let mut to_remove = Vec::new();
2212 for &cid in &self.learned_clause_ids {
2213 if cid == new_clause_id {
2214 continue;
2215 }
2216
2217 if let Some(clause) = self.clauses.get(cid) {
2218 if clause.deleted || clause.lits.len() < new_clause.len() {
2219 continue;
2220 }
2221
2222 if new_clause.iter().all(|&lit| clause.lits.contains(&lit)) {
2224 to_remove.push(cid);
2225 }
2226 }
2227 }
2228
2229 for cid in to_remove {
2231 self.clauses.remove(cid);
2232 self.stats.deleted_clauses += 1;
2233 }
2234 }
2235
2236 fn handle_clause_deletion_and_restart(&mut self) {
2238 self.conflicts_since_deletion += 1;
2239
2240 if self.conflicts_since_deletion >= self.config.clause_deletion_threshold as u64 {
2241 self.reduce_clause_database();
2242 self.conflicts_since_deletion = 0;
2243 }
2244
2245 if self.stats.conflicts >= self.restart_threshold {
2246 self.restart();
2247 }
2248 }
2249
2250 #[must_use]
2252 pub fn trail(&self) -> &Trail {
2253 &self.trail
2254 }
2255
2256 #[must_use]
2258 pub fn decision_level(&self) -> u32 {
2259 self.trail.decision_level()
2260 }
2261
2262 fn rand_u64(&mut self) -> u64 {
2264 let mut x = self.rng_state;
2265 x ^= x << 13;
2266 x ^= x >> 7;
2267 x ^= x << 17;
2268 self.rng_state = x;
2269 x
2270 }
2271
2272 fn rand_f64(&mut self) -> f64 {
2274 const MAX: f64 = u64::MAX as f64;
2275 (self.rand_u64() as f64) / MAX
2276 }
2277
2278 fn rand_bool(&mut self, probability: f64) -> bool {
2280 self.rand_f64() < probability
2281 }
2282
2283 fn check_hyper_binary_resolution(&mut self, _lit: Lit, implied: Lit, reason_id: ClauseId) {
2287 if self.trail.decision_level() < 2 {
2289 return;
2290 }
2291
2292 let reason_clause = match self.clauses.get(reason_id) {
2294 Some(c) if c.lits.len() >= 2 && c.lits.len() <= 4 => c.lits.clone(),
2295 _ => return,
2296 };
2297
2298 let current_level = self.trail.decision_level();
2301 let mut current_level_lits = SmallVec::<[Lit; 4]>::new();
2302 let mut has_non_zero_level_other = false;
2303
2304 for &reason_lit in &reason_clause {
2305 if reason_lit != implied {
2306 let var = reason_lit.var();
2307 let level = self.trail.level(var);
2308 if level == current_level {
2309 current_level_lits.push(reason_lit);
2310 } else if level > 0 {
2311 has_non_zero_level_other = true;
2315 }
2316 }
2317 }
2318
2319 if current_level_lits.len() == 1 && !has_non_zero_level_other {
2324 let other_lit = current_level_lits[0];
2325
2326 let binary_clause_lits = [other_lit, implied];
2330
2331 if !self.has_binary_implication(other_lit.negate(), implied) {
2335 let clause_id = self.clauses.add_learned(binary_clause_lits.iter().copied());
2337 self.binary_graph
2339 .add(other_lit.negate(), implied, clause_id);
2340 self.binary_graph
2341 .add(implied.negate(), other_lit, clause_id);
2342 self.stats.learned_clauses += 1;
2343 }
2344 }
2345 }
2346
2347 fn has_binary_implication(&self, from_lit: Lit, to_lit: Lit) -> bool {
2349 self.binary_graph
2350 .get(from_lit)
2351 .iter()
2352 .any(|(lit, _)| *lit == to_lit)
2353 }
2354
2355 fn vivify_clauses(&mut self) {
2358 if self.trail.decision_level() != 0 {
2359 return; }
2361
2362 let mut vivified_count = 0;
2363 let max_vivifications = 100; let clause_ids: Vec<ClauseId> = self
2367 .learned_clause_ids
2368 .iter()
2369 .copied()
2370 .take(max_vivifications)
2371 .collect();
2372
2373 for clause_id in clause_ids {
2374 if vivified_count >= max_vivifications {
2375 break;
2376 }
2377
2378 let clause_lits = match self.clauses.get(clause_id) {
2379 Some(c) if !c.deleted && c.lits.len() > 2 => c.lits.clone(),
2380 _ => continue,
2381 };
2382
2383 for skip_idx in 0..clause_lits.len() {
2386 let saved_level = self.trail.decision_level();
2388
2389 self.trail.new_decision_level();
2391 let mut conflict = false;
2392
2393 for (i, &lit) in clause_lits.iter().enumerate() {
2394 if i == skip_idx {
2395 continue;
2396 }
2397
2398 let value = self.trail.lit_value(lit);
2399 if value.is_true() {
2400 conflict = false;
2402 break;
2403 } else if value.is_false() {
2404 continue;
2406 } else {
2407 self.trail.assign_decision(lit.negate());
2409
2410 if self.propagate().is_some() {
2412 conflict = true;
2413 break;
2414 }
2415 }
2416 }
2417
2418 self.backtrack(saved_level);
2420
2421 if conflict
2422 && let Some(clause) = self.clauses.get_mut(clause_id)
2423 && clause.lits.len() > 2
2424 {
2425 clause.lits.remove(skip_idx);
2428 vivified_count += 1;
2429 break; }
2431 }
2432 }
2433 }
2434
2435 fn inprocess(&mut self) {
2437 use crate::Preprocessor;
2438
2439 if self.trail.decision_level() != 0 {
2441 return;
2442 }
2443
2444 let mut preprocessor = Preprocessor::new(self.num_vars);
2446
2447 let _pure_elim = preprocessor.pure_literal_elimination(&mut self.clauses);
2449 let _subsumption = preprocessor.subsumption_elimination(&mut self.clauses);
2450
2451 self.strengthen_clauses_inprocessing();
2453
2454 }
2458
2459 fn strengthen_clauses_inprocessing(&mut self) {
2464 if self.trail.decision_level() != 0 {
2465 return;
2466 }
2467
2468 let max_clauses_to_strengthen = 50; let mut strengthened_count = 0;
2470
2471 let mut candidates: Vec<(ClauseId, u32)> = Vec::new();
2473
2474 for &clause_id in &self.learned_clause_ids {
2475 if let Some(clause) = self.clauses.get(clause_id)
2476 && !clause.deleted
2477 && clause.lits.len() > 3
2478 && clause.lbd > 2
2479 {
2480 candidates.push((clause_id, clause.lbd));
2481 }
2482 }
2483
2484 candidates.sort_by_key(|(_, lbd)| std::cmp::Reverse(*lbd));
2486
2487 for (clause_id, _) in candidates.iter().take(max_clauses_to_strengthen) {
2488 if strengthened_count >= max_clauses_to_strengthen {
2489 break;
2490 }
2491
2492 let clause_lits = match self.clauses.get(*clause_id) {
2493 Some(c) if !c.deleted && c.lits.len() > 3 => c.lits.clone(),
2494 _ => continue,
2495 };
2496
2497 let mut literals_to_remove = Vec::new();
2499
2500 for (i, &lit) in clause_lits.iter().enumerate() {
2501 let saved_level = self.trail.decision_level();
2503
2504 self.trail.new_decision_level();
2506 self.trail.assign_decision(lit.negate());
2507
2508 let conflict = self.propagate();
2510
2511 self.backtrack(saved_level);
2513
2514 if conflict.is_some() {
2515 let mut remaining: Vec<Lit> = clause_lits
2522 .iter()
2523 .enumerate()
2524 .filter(|(j, _)| *j != i)
2525 .map(|(_, &l)| l)
2526 .collect();
2527
2528 if remaining.len() >= 2 {
2530 remaining.sort_by_key(|l| l.code());
2532 let mut is_tautology = false;
2533 for k in 0..remaining.len() - 1 {
2534 if remaining[k] == remaining[k + 1].negate() {
2535 is_tautology = true;
2536 break;
2537 }
2538 }
2539
2540 if !is_tautology {
2541 literals_to_remove.push(i);
2542 break; }
2544 }
2545 }
2546 }
2547
2548 if !literals_to_remove.is_empty() {
2550 if let Some(clause) = self.clauses.get_mut(*clause_id) {
2552 for &idx in literals_to_remove.iter().rev() {
2554 if idx < clause.lits.len() {
2555 clause.lits.remove(idx);
2556 }
2557 }
2558 }
2559
2560 if let Some(clause) = self.clauses.get(*clause_id) {
2562 let lits_clone = clause.lits.clone();
2563 let new_lbd = self.compute_lbd(&lits_clone);
2564
2565 if let Some(clause) = self.clauses.get_mut(*clause_id) {
2567 clause.lbd = new_lbd;
2568 }
2569
2570 strengthened_count += 1;
2571 }
2572 }
2573 }
2574 }
2575
2576 pub fn debug_print_learned_clauses(&self) {
2578 println!(
2579 "=== Learned Clauses ({}) ===",
2580 self.learned_clause_ids.len()
2581 );
2582 for (i, &cid) in self.learned_clause_ids.iter().enumerate() {
2583 if let Some(clause) = self.clauses.get(cid)
2584 && !clause.deleted
2585 {
2586 let lits: Vec<String> = clause
2587 .lits
2588 .iter()
2589 .map(|lit| {
2590 let var = lit.var().index();
2591 if lit.is_pos() {
2592 format!("v{}", var)
2593 } else {
2594 format!("~v{}", var)
2595 }
2596 })
2597 .collect();
2598 println!(
2599 " Learned {}: ({}), LBD={}",
2600 i,
2601 lits.join(" | "),
2602 clause.lbd
2603 );
2604 }
2605 }
2606 }
2607
2608 pub fn debug_print_binary_graph(&self) {
2610 println!("=== Binary Implication Graph ===");
2611 for lit_code in 0..(self.num_vars * 2) {
2612 let lit = Lit::from_code(lit_code as u32);
2613 let implications = self.binary_graph.get(lit);
2614 if !implications.is_empty() {
2615 let lit_str = if lit.is_pos() {
2616 format!("v{}", lit.var().index())
2617 } else {
2618 format!("~v{}", lit.var().index())
2619 };
2620 for &(implied, _cid) in implications {
2621 let impl_str = if implied.is_pos() {
2622 format!("v{}", implied.var().index())
2623 } else {
2624 format!("~v{}", implied.var().index())
2625 };
2626 println!(" {} -> {}", lit_str, impl_str);
2627 }
2628 }
2629 }
2630 }
2631}
2632
2633#[cfg(test)]
2634mod tests {
2635 use super::*;
2636
2637 #[test]
2638 fn test_empty_sat() {
2639 let mut solver = Solver::new();
2640 assert_eq!(solver.solve(), SolverResult::Sat);
2641 }
2642
2643 #[test]
2644 fn test_simple_sat() {
2645 let mut solver = Solver::new();
2646 let _x = solver.new_var();
2647 let _y = solver.new_var();
2648
2649 solver.add_clause_dimacs(&[1, 2]);
2651 solver.add_clause_dimacs(&[-1, 2]);
2653
2654 assert_eq!(solver.solve(), SolverResult::Sat);
2655 assert!(solver.model_value(Var::new(1)).is_true()); }
2657
2658 #[test]
2659 fn test_simple_unsat() {
2660 let mut solver = Solver::new();
2661 let _x = solver.new_var();
2662
2663 solver.add_clause_dimacs(&[1]);
2665 solver.add_clause_dimacs(&[-1]);
2667
2668 assert_eq!(solver.solve(), SolverResult::Unsat);
2669 }
2670
2671 #[test]
2672 fn test_pigeonhole_2_1() {
2673 let mut solver = Solver::new();
2675 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);
2686 }
2687
2688 #[test]
2689 fn test_3sat_random() {
2690 let mut solver = Solver::new();
2691 for _ in 0..10 {
2692 solver.new_var();
2693 }
2694
2695 solver.add_clause_dimacs(&[1, 2, 3]);
2697 solver.add_clause_dimacs(&[-1, 4, 5]);
2698 solver.add_clause_dimacs(&[2, -3, 6]);
2699 solver.add_clause_dimacs(&[-4, 7, 8]);
2700 solver.add_clause_dimacs(&[5, -6, 9]);
2701 solver.add_clause_dimacs(&[-7, 8, 10]);
2702 solver.add_clause_dimacs(&[1, -8, -9]);
2703 solver.add_clause_dimacs(&[-2, 3, -10]);
2704
2705 let result = solver.solve();
2706 assert_eq!(result, SolverResult::Sat);
2707 }
2708
2709 #[test]
2710 fn test_luby_sequence() {
2711 assert_eq!(Solver::luby(0), 1);
2713 assert_eq!(Solver::luby(1), 1);
2714 assert_eq!(Solver::luby(2), 2);
2715 assert_eq!(Solver::luby(3), 1);
2716 assert_eq!(Solver::luby(4), 1);
2717 assert_eq!(Solver::luby(5), 2);
2718 assert_eq!(Solver::luby(6), 4);
2719 assert_eq!(Solver::luby(7), 1);
2720 }
2721
2722 #[test]
2723 fn test_phase_saving() {
2724 let mut solver = Solver::new();
2725 for _ in 0..5 {
2726 solver.new_var();
2727 }
2728
2729 solver.add_clause_dimacs(&[1, 2]);
2731 solver.add_clause_dimacs(&[-1, 3]);
2732 solver.add_clause_dimacs(&[-2, 4]);
2733 solver.add_clause_dimacs(&[-3, -4, 5]);
2734 solver.add_clause_dimacs(&[-5, 1]);
2735
2736 let result = solver.solve();
2737 assert_eq!(result, SolverResult::Sat);
2738 }
2739
2740 #[test]
2741 fn test_lbd_computation() {
2742 let mut solver = Solver::with_config(SolverConfig {
2744 clause_deletion_threshold: 5, ..SolverConfig::default()
2746 });
2747
2748 for _ in 0..20 {
2749 solver.new_var();
2750 }
2751
2752 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();
2771 assert_eq!(result, SolverResult::Unsat);
2772 assert!(solver.stats().conflicts > 0);
2774 }
2775
2776 #[test]
2777 fn test_clause_activity_decay() {
2778 let mut solver = Solver::new();
2779 for _ in 0..10 {
2780 solver.new_var();
2781 }
2782
2783 solver.add_clause_dimacs(&[1, 2, 3]);
2785 solver.add_clause_dimacs(&[-1, 4, 5]);
2786 solver.add_clause_dimacs(&[-2, -3, 6]);
2787
2788 let result = solver.solve();
2790 assert_eq!(result, SolverResult::Sat);
2791 }
2792
2793 #[test]
2794 fn test_clause_minimization() {
2795 let mut solver = Solver::new();
2798
2799 for _ in 0..15 {
2800 solver.new_var();
2801 }
2802
2803 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]);
2820 solver.add_clause_dimacs(&[-2, -12]);
2821 solver.add_clause_dimacs(&[-7, -12]);
2822
2823 solver.add_clause_dimacs(&[-3, -8]);
2824 solver.add_clause_dimacs(&[-3, -13]);
2825 solver.add_clause_dimacs(&[-8, -13]);
2826
2827 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]);
2834 solver.add_clause_dimacs(&[-12, -13]);
2835
2836 let result = solver.solve();
2837 assert_eq!(result, SolverResult::Sat);
2838
2839 }
2843
2844 struct NullTheory;
2846
2847 impl TheoryCallback for NullTheory {
2848 fn on_assignment(&mut self, _lit: Lit) -> TheoryCheckResult {
2849 TheoryCheckResult::Sat
2850 }
2851
2852 fn final_check(&mut self) -> TheoryCheckResult {
2853 TheoryCheckResult::Sat
2854 }
2855
2856 fn on_backtrack(&mut self, _level: u32) {}
2857 }
2858
2859 #[test]
2860 fn test_solve_with_theory_sat() {
2861 let mut solver = Solver::new();
2862 let mut theory = NullTheory;
2863
2864 let _x = solver.new_var();
2865 let _y = solver.new_var();
2866
2867 solver.add_clause_dimacs(&[1, 2]);
2869 solver.add_clause_dimacs(&[-1, 2]);
2871
2872 assert_eq!(solver.solve_with_theory(&mut theory), SolverResult::Sat);
2873 assert!(solver.model_value(Var::new(1)).is_true()); }
2875
2876 #[test]
2877 fn test_solve_with_theory_unsat() {
2878 let mut solver = Solver::new();
2879 let mut theory = NullTheory;
2880
2881 let _x = solver.new_var();
2882
2883 solver.add_clause_dimacs(&[1]);
2885 solver.add_clause_dimacs(&[-1]);
2887
2888 assert_eq!(solver.solve_with_theory(&mut theory), SolverResult::Unsat);
2889 }
2890
2891 struct ImplicationTheory {
2893 x0_true: bool,
2895 }
2896
2897 impl ImplicationTheory {
2898 fn new() -> Self {
2899 Self { x0_true: false }
2900 }
2901 }
2902
2903 impl TheoryCallback for ImplicationTheory {
2904 fn on_assignment(&mut self, lit: Lit) -> TheoryCheckResult {
2905 if lit.var().index() == 0 && lit.is_pos() {
2907 self.x0_true = true;
2908 let reason: SmallVec<[Lit; 8]> = smallvec::smallvec![Lit::pos(Var::new(0))];
2911 return TheoryCheckResult::Propagated(vec![(Lit::pos(Var::new(1)), reason)]);
2912 }
2913 TheoryCheckResult::Sat
2914 }
2915
2916 fn final_check(&mut self) -> TheoryCheckResult {
2917 TheoryCheckResult::Sat
2918 }
2919
2920 fn on_backtrack(&mut self, _level: u32) {
2921 self.x0_true = false;
2922 }
2923 }
2924
2925 #[test]
2926 fn test_theory_propagation() {
2927 let mut solver = Solver::new();
2928 let mut theory = ImplicationTheory::new();
2929
2930 let _x0 = solver.new_var();
2931 let _x1 = solver.new_var();
2932
2933 solver.add_clause_dimacs(&[1]);
2935
2936 let result = solver.solve_with_theory(&mut theory);
2937 assert_eq!(result, SolverResult::Sat);
2938
2939 assert!(solver.model_value(Var::new(0)).is_true());
2941 assert!(solver.model_value(Var::new(1)).is_true());
2943 }
2944
2945 struct MutexTheory {
2947 x0_true: Option<Lit>,
2948 x1_true: Option<Lit>,
2949 }
2950
2951 impl MutexTheory {
2952 fn new() -> Self {
2953 Self {
2954 x0_true: None,
2955 x1_true: None,
2956 }
2957 }
2958 }
2959
2960 impl TheoryCallback for MutexTheory {
2961 fn on_assignment(&mut self, lit: Lit) -> TheoryCheckResult {
2962 if lit.var().index() == 0 && lit.is_pos() {
2963 self.x0_true = Some(lit);
2964 }
2965 if lit.var().index() == 1 && lit.is_pos() {
2966 self.x1_true = Some(lit);
2967 }
2968
2969 if self.x0_true.is_some() && self.x1_true.is_some() {
2971 let conflict: SmallVec<[Lit; 8]> = smallvec::smallvec![
2973 Lit::pos(Var::new(0)), Lit::pos(Var::new(1)) ];
2976 return TheoryCheckResult::Conflict(conflict);
2977 }
2978 TheoryCheckResult::Sat
2979 }
2980
2981 fn final_check(&mut self) -> TheoryCheckResult {
2982 if self.x0_true.is_some() && self.x1_true.is_some() {
2983 let conflict: SmallVec<[Lit; 8]> =
2984 smallvec::smallvec![Lit::pos(Var::new(0)), Lit::pos(Var::new(1))];
2985 return TheoryCheckResult::Conflict(conflict);
2986 }
2987 TheoryCheckResult::Sat
2988 }
2989
2990 fn on_backtrack(&mut self, _level: u32) {
2991 self.x0_true = None;
2992 self.x1_true = None;
2993 }
2994 }
2995
2996 #[test]
2997 fn test_theory_conflict() {
2998 let mut solver = Solver::new();
2999 let mut theory = MutexTheory::new();
3000
3001 let _x0 = solver.new_var();
3002 let _x1 = solver.new_var();
3003
3004 solver.add_clause_dimacs(&[1]);
3006 solver.add_clause_dimacs(&[2]);
3007
3008 let result = solver.solve_with_theory(&mut theory);
3009 assert_eq!(result, SolverResult::Unsat);
3010 }
3011
3012 #[test]
3013 fn test_solve_with_assumptions_sat() {
3014 let mut solver = Solver::new();
3015
3016 let x0 = solver.new_var();
3017 let x1 = solver.new_var();
3018
3019 solver.add_clause([Lit::pos(x0), Lit::pos(x1)]);
3021
3022 let assumptions = [Lit::pos(x0)];
3024 let (result, core) = solver.solve_with_assumptions(&assumptions);
3025
3026 assert_eq!(result, SolverResult::Sat);
3027 assert!(core.is_none());
3028 }
3029
3030 #[test]
3031 fn test_solve_with_assumptions_unsat() {
3032 let mut solver = Solver::new();
3033
3034 let x0 = solver.new_var();
3035 let x1 = solver.new_var();
3036
3037 solver.add_clause([Lit::neg(x0), Lit::neg(x1)]);
3039
3040 let assumptions = [Lit::pos(x0), Lit::pos(x1)];
3042 let (result, core) = solver.solve_with_assumptions(&assumptions);
3043
3044 assert_eq!(result, SolverResult::Unsat);
3045 assert!(core.is_some());
3046 let core = core.expect("UNSAT result must have conflict core");
3047 assert!(!core.is_empty());
3049 }
3050
3051 #[test]
3052 fn test_solve_with_assumptions_core_extraction() {
3053 let mut solver = Solver::new();
3054
3055 let x0 = solver.new_var();
3056 let x1 = solver.new_var();
3057 let x2 = solver.new_var();
3058
3059 solver.add_clause([Lit::neg(x0)]);
3061
3062 let assumptions = [Lit::pos(x0), Lit::pos(x1), Lit::pos(x2)];
3065 let (result, core) = solver.solve_with_assumptions(&assumptions);
3066
3067 assert_eq!(result, SolverResult::Unsat);
3068 assert!(core.is_some());
3069 let core = core.expect("UNSAT result must have conflict core");
3070 assert!(core.contains(&Lit::pos(x0)));
3072 }
3073
3074 #[test]
3075 fn test_solve_with_assumptions_incremental() {
3076 let mut solver = Solver::new();
3077
3078 let x0 = solver.new_var();
3079 let x1 = solver.new_var();
3080
3081 solver.add_clause([Lit::pos(x0), Lit::pos(x1)]);
3083
3084 let (result1, _) = solver.solve_with_assumptions(&[Lit::neg(x0)]);
3086 assert_eq!(result1, SolverResult::Sat);
3087
3088 let (result2, core2) = solver.solve_with_assumptions(&[Lit::neg(x0), Lit::neg(x1)]);
3090 assert_eq!(result2, SolverResult::Unsat);
3091 assert!(core2.is_some());
3092
3093 let (result3, _) = solver.solve_with_assumptions(&[Lit::pos(x0)]);
3095 assert_eq!(result3, SolverResult::Sat);
3096 }
3097
3098 #[test]
3099 fn test_push_pop_simple() {
3100 let mut solver = Solver::new();
3101
3102 let x0 = solver.new_var();
3103
3104 assert_eq!(solver.solve(), SolverResult::Sat);
3106
3107 solver.push();
3109 solver.add_clause([Lit::pos(x0)]);
3110 assert_eq!(solver.solve(), SolverResult::Sat);
3111 assert!(solver.model_value(x0).is_true());
3112
3113 solver.pop();
3115 let result = solver.solve();
3116 assert_eq!(
3117 result,
3118 SolverResult::Sat,
3119 "After pop, expected SAT but got {:?}. trivially_unsat={}",
3120 result,
3121 solver.trivially_unsat
3122 );
3123 }
3124
3125 #[test]
3126 fn test_push_pop_incremental() {
3127 let mut solver = Solver::new();
3128
3129 let x0 = solver.new_var();
3130 let x1 = solver.new_var();
3131 let x2 = solver.new_var();
3132
3133 solver.add_clause([Lit::pos(x0), Lit::pos(x1)]);
3135 assert_eq!(solver.solve(), SolverResult::Sat);
3136
3137 solver.push();
3139 solver.add_clause([Lit::neg(x0)]);
3140 assert_eq!(solver.solve(), SolverResult::Sat);
3141 assert!(solver.model_value(x1).is_true());
3143
3144 solver.push();
3146 solver.add_clause([Lit::neg(x1)]);
3147 assert_eq!(solver.solve(), SolverResult::Unsat);
3148
3149 solver.pop();
3151 assert_eq!(solver.solve(), SolverResult::Sat);
3152 assert!(solver.model_value(x1).is_true());
3153
3154 solver.pop();
3156 assert_eq!(solver.solve(), SolverResult::Sat);
3157 solver.push();
3161 solver.add_clause([Lit::pos(x0)]);
3162 solver.add_clause([Lit::pos(x2)]);
3163 assert_eq!(solver.solve(), SolverResult::Sat);
3164 assert!(solver.model_value(x0).is_true());
3165 assert!(solver.model_value(x2).is_true());
3166
3167 solver.pop();
3169 assert_eq!(solver.solve(), SolverResult::Sat);
3170 }
3171
3172 #[test]
3173 fn test_push_pop_with_learned_clauses() {
3174 let mut solver = Solver::new();
3175
3176 let x0 = solver.new_var();
3177 let x1 = solver.new_var();
3178 let x2 = solver.new_var();
3179
3180 solver.add_clause([Lit::pos(x0), Lit::pos(x1)]);
3183 solver.add_clause([Lit::neg(x0), Lit::pos(x2)]);
3184 solver.add_clause([Lit::neg(x1), Lit::pos(x2)]);
3185
3186 assert_eq!(solver.solve(), SolverResult::Sat);
3187
3188 solver.push();
3190 solver.add_clause([Lit::neg(x2)]);
3191
3192 assert_eq!(solver.solve(), SolverResult::Unsat);
3194
3195 solver.pop();
3197
3198 assert_eq!(solver.solve(), SolverResult::Sat);
3200 }
3201}