1use crate::mbqi::{MBQIResult, MBQISolver};
4use crate::simplify::Simplifier;
5use num_rational::Rational64;
6use num_traits::{One, ToPrimitive, Zero};
7use oxiz_core::ast::{TermId, TermKind, TermManager};
8use oxiz_sat::{
9 Lit, RestartStrategy, Solver as SatSolver, SolverConfig as SatConfig,
10 SolverResult as SatResult, TheoryCallback, TheoryCheckResult, Var,
11};
12use oxiz_theories::arithmetic::ArithSolver;
13use oxiz_theories::bv::BvSolver;
14use oxiz_theories::euf::EufSolver;
15use oxiz_theories::{EqualityNotification, Theory, TheoryCombination};
16use rustc_hash::{FxHashMap, FxHashSet};
17use smallvec::SmallVec;
18
19#[derive(Debug, Clone)]
21pub enum ProofStep {
22 Input {
24 index: u32,
26 clause: Vec<Lit>,
28 },
29 Resolution {
31 index: u32,
33 left: u32,
35 right: u32,
37 pivot: Var,
39 clause: Vec<Lit>,
41 },
42 TheoryLemma {
44 index: u32,
46 theory: String,
48 clause: Vec<Lit>,
50 explanation: Vec<TermId>,
52 },
53}
54
55#[derive(Debug, Clone)]
57pub struct Proof {
58 steps: Vec<ProofStep>,
60 empty_clause_index: Option<u32>,
62}
63
64impl Proof {
65 #[must_use]
67 pub fn new() -> Self {
68 Self {
69 steps: Vec::new(),
70 empty_clause_index: None,
71 }
72 }
73
74 pub fn add_step(&mut self, step: ProofStep) {
76 self.steps.push(step);
77 }
78
79 pub fn set_empty_clause(&mut self, index: u32) {
81 self.empty_clause_index = Some(index);
82 }
83
84 #[must_use]
86 pub fn is_complete(&self) -> bool {
87 self.empty_clause_index.is_some()
88 }
89
90 #[must_use]
92 pub fn len(&self) -> usize {
93 self.steps.len()
94 }
95
96 #[must_use]
98 pub fn is_empty(&self) -> bool {
99 self.steps.is_empty()
100 }
101
102 #[must_use]
104 pub fn format(&self) -> String {
105 let mut result = String::from("(proof\n");
106 for step in &self.steps {
107 match step {
108 ProofStep::Input { index, clause } => {
109 result.push_str(&format!(" (input {} {:?})\n", index, clause));
110 }
111 ProofStep::Resolution {
112 index,
113 left,
114 right,
115 pivot,
116 clause,
117 } => {
118 result.push_str(&format!(
119 " (resolution {} {} {} {:?} {:?})\n",
120 index, left, right, pivot, clause
121 ));
122 }
123 ProofStep::TheoryLemma {
124 index,
125 theory,
126 clause,
127 ..
128 } => {
129 result.push_str(&format!(
130 " (theory-lemma {} {} {:?})\n",
131 index, theory, clause
132 ));
133 }
134 }
135 }
136 if let Some(idx) = self.empty_clause_index {
137 result.push_str(&format!(" (empty-clause {})\n", idx));
138 }
139 result.push_str(")\n");
140 result
141 }
142}
143
144impl Default for Proof {
145 fn default() -> Self {
146 Self::new()
147 }
148}
149
150#[derive(Debug, Clone)]
152#[allow(dead_code)]
153enum Constraint {
154 Eq(TermId, TermId),
156 Diseq(TermId, TermId),
158 Lt(TermId, TermId),
160 Le(TermId, TermId),
162 Gt(TermId, TermId),
164 Ge(TermId, TermId),
166}
167
168#[derive(Debug, Clone, Copy, PartialEq, Eq)]
170enum ArithConstraintType {
171 Lt,
173 Le,
175 Gt,
177 Ge,
179}
180
181#[derive(Debug, Clone)]
184struct ParsedArithConstraint {
185 terms: SmallVec<[(TermId, Rational64); 4]>,
187 constant: Rational64,
189 constraint_type: ArithConstraintType,
191 reason_term: TermId,
193}
194
195#[derive(Debug, Clone, Copy, PartialEq, Eq)]
197enum Polarity {
198 Positive,
200 Negative,
202 Both,
204}
205
206#[derive(Debug, Clone, Copy, PartialEq, Eq)]
208pub enum SolverResult {
209 Sat,
211 Unsat,
213 Unknown,
215}
216
217#[derive(Debug, Clone, Copy, PartialEq, Eq)]
219pub enum TheoryMode {
220 Eager,
222 Lazy,
224}
225
226#[derive(Debug, Clone)]
228pub struct SolverConfig {
229 pub timeout_ms: u64,
231 pub parallel: bool,
233 pub num_threads: usize,
235 pub proof: bool,
237 pub model: bool,
239 pub theory_mode: TheoryMode,
241 pub simplify: bool,
243 pub max_conflicts: u64,
245 pub max_decisions: u64,
247 pub restart_strategy: RestartStrategy,
249 pub enable_clause_minimization: bool,
251 pub enable_clause_subsumption: bool,
253 pub enable_variable_elimination: bool,
255 pub variable_elimination_limit: usize,
257 pub enable_blocked_clause_elimination: bool,
259 pub enable_symmetry_breaking: bool,
261 pub enable_inprocessing: bool,
263 pub inprocessing_interval: u64,
265}
266
267impl Default for SolverConfig {
268 fn default() -> Self {
269 Self::balanced()
270 }
271}
272
273impl SolverConfig {
274 #[must_use]
277 pub fn fast() -> Self {
278 Self {
279 timeout_ms: 0,
280 parallel: false,
281 num_threads: 4,
282 proof: false,
283 model: true,
284 theory_mode: TheoryMode::Eager,
285 simplify: true, max_conflicts: 0,
287 max_decisions: 0,
288 restart_strategy: RestartStrategy::Geometric, enable_clause_minimization: true, enable_clause_subsumption: false, enable_variable_elimination: false, variable_elimination_limit: 0,
293 enable_blocked_clause_elimination: false, enable_symmetry_breaking: false,
295 enable_inprocessing: false, inprocessing_interval: 0,
297 }
298 }
299
300 #[must_use]
303 pub fn balanced() -> Self {
304 Self {
305 timeout_ms: 0,
306 parallel: false,
307 num_threads: 4,
308 proof: false,
309 model: true,
310 theory_mode: TheoryMode::Eager,
311 simplify: true,
312 max_conflicts: 0,
313 max_decisions: 0,
314 restart_strategy: RestartStrategy::Glucose, enable_clause_minimization: true,
316 enable_clause_subsumption: true,
317 enable_variable_elimination: true,
318 variable_elimination_limit: 1000, enable_blocked_clause_elimination: true,
320 enable_symmetry_breaking: false, enable_inprocessing: true,
322 inprocessing_interval: 10000,
323 }
324 }
325
326 #[must_use]
329 pub fn thorough() -> Self {
330 Self {
331 timeout_ms: 0,
332 parallel: false,
333 num_threads: 4,
334 proof: false,
335 model: true,
336 theory_mode: TheoryMode::Eager,
337 simplify: true,
338 max_conflicts: 0,
339 max_decisions: 0,
340 restart_strategy: RestartStrategy::Glucose,
341 enable_clause_minimization: true,
342 enable_clause_subsumption: true,
343 enable_variable_elimination: true,
344 variable_elimination_limit: 5000, enable_blocked_clause_elimination: true,
346 enable_symmetry_breaking: true, enable_inprocessing: true,
348 inprocessing_interval: 5000, }
350 }
351
352 #[must_use]
355 pub fn minimal() -> Self {
356 Self {
357 timeout_ms: 0,
358 parallel: false,
359 num_threads: 1,
360 proof: false,
361 model: true,
362 theory_mode: TheoryMode::Lazy, simplify: false,
364 max_conflicts: 0,
365 max_decisions: 0,
366 restart_strategy: RestartStrategy::Geometric,
367 enable_clause_minimization: false,
368 enable_clause_subsumption: false,
369 enable_variable_elimination: false,
370 variable_elimination_limit: 0,
371 enable_blocked_clause_elimination: false,
372 enable_symmetry_breaking: false,
373 enable_inprocessing: false,
374 inprocessing_interval: 0,
375 }
376 }
377
378 #[must_use]
380 pub fn with_proof(mut self) -> Self {
381 self.proof = true;
382 self
383 }
384
385 #[must_use]
387 pub fn with_timeout(mut self, timeout_ms: u64) -> Self {
388 self.timeout_ms = timeout_ms;
389 self
390 }
391
392 #[must_use]
394 pub fn with_max_conflicts(mut self, max_conflicts: u64) -> Self {
395 self.max_conflicts = max_conflicts;
396 self
397 }
398
399 #[must_use]
401 pub fn with_max_decisions(mut self, max_decisions: u64) -> Self {
402 self.max_decisions = max_decisions;
403 self
404 }
405
406 #[must_use]
408 pub fn with_parallel(mut self, num_threads: usize) -> Self {
409 self.parallel = true;
410 self.num_threads = num_threads;
411 self
412 }
413
414 #[must_use]
416 pub fn with_restart_strategy(mut self, strategy: RestartStrategy) -> Self {
417 self.restart_strategy = strategy;
418 self
419 }
420
421 #[must_use]
423 pub fn with_theory_mode(mut self, mode: TheoryMode) -> Self {
424 self.theory_mode = mode;
425 self
426 }
427}
428
429#[derive(Debug, Clone, Default)]
431pub struct Statistics {
432 pub decisions: u64,
434 pub conflicts: u64,
436 pub propagations: u64,
438 pub restarts: u64,
440 pub learned_clauses: u64,
442 pub theory_propagations: u64,
444 pub theory_conflicts: u64,
446}
447
448impl Statistics {
449 #[must_use]
451 pub fn new() -> Self {
452 Self::default()
453 }
454
455 pub fn reset(&mut self) {
457 *self = Self::default();
458 }
459}
460
461#[derive(Debug, Clone)]
463pub struct Model {
464 assignments: FxHashMap<TermId, TermId>,
466}
467
468impl Model {
469 #[must_use]
471 pub fn new() -> Self {
472 Self {
473 assignments: FxHashMap::default(),
474 }
475 }
476
477 #[must_use]
479 pub fn get(&self, term: TermId) -> Option<TermId> {
480 self.assignments.get(&term).copied()
481 }
482
483 pub fn set(&mut self, term: TermId, value: TermId) {
485 self.assignments.insert(term, value);
486 }
487
488 pub fn minimize(&self, essential_vars: &[TermId]) -> Model {
491 let mut minimized = Model::new();
492
493 for &var in essential_vars {
495 if let Some(&value) = self.assignments.get(&var) {
496 minimized.set(var, value);
497 }
498 }
499
500 minimized
501 }
502
503 #[must_use]
505 pub fn size(&self) -> usize {
506 self.assignments.len()
507 }
508
509 #[must_use]
511 pub fn assignments(&self) -> &FxHashMap<TermId, TermId> {
512 &self.assignments
513 }
514
515 pub fn eval(&self, term: TermId, manager: &mut TermManager) -> TermId {
518 if let Some(val) = self.get(term) {
520 return val;
521 }
522
523 let Some(t) = manager.get(term).cloned() else {
525 return term;
526 };
527
528 match t.kind {
529 TermKind::True
531 | TermKind::False
532 | TermKind::IntConst(_)
533 | TermKind::RealConst(_)
534 | TermKind::BitVecConst { .. } => term,
535
536 TermKind::Var(_) => self.get(term).unwrap_or(term),
538
539 TermKind::Not(arg) => {
541 let arg_val = self.eval(arg, manager);
542 if let Some(t) = manager.get(arg_val) {
543 match t.kind {
544 TermKind::True => manager.mk_false(),
545 TermKind::False => manager.mk_true(),
546 _ => manager.mk_not(arg_val),
547 }
548 } else {
549 manager.mk_not(arg_val)
550 }
551 }
552
553 TermKind::And(ref args) => {
554 let mut eval_args = Vec::new();
555 for &arg in args {
556 let val = self.eval(arg, manager);
557 if let Some(t) = manager.get(val) {
558 if matches!(t.kind, TermKind::False) {
559 return manager.mk_false();
560 }
561 if !matches!(t.kind, TermKind::True) {
562 eval_args.push(val);
563 }
564 } else {
565 eval_args.push(val);
566 }
567 }
568 if eval_args.is_empty() {
569 manager.mk_true()
570 } else if eval_args.len() == 1 {
571 eval_args[0]
572 } else {
573 manager.mk_and(eval_args)
574 }
575 }
576
577 TermKind::Or(ref args) => {
578 let mut eval_args = Vec::new();
579 for &arg in args {
580 let val = self.eval(arg, manager);
581 if let Some(t) = manager.get(val) {
582 if matches!(t.kind, TermKind::True) {
583 return manager.mk_true();
584 }
585 if !matches!(t.kind, TermKind::False) {
586 eval_args.push(val);
587 }
588 } else {
589 eval_args.push(val);
590 }
591 }
592 if eval_args.is_empty() {
593 manager.mk_false()
594 } else if eval_args.len() == 1 {
595 eval_args[0]
596 } else {
597 manager.mk_or(eval_args)
598 }
599 }
600
601 TermKind::Implies(lhs, rhs) => {
602 let lhs_val = self.eval(lhs, manager);
603 let rhs_val = self.eval(rhs, manager);
604
605 if let Some(t) = manager.get(lhs_val) {
606 if matches!(t.kind, TermKind::False) {
607 return manager.mk_true();
608 }
609 if matches!(t.kind, TermKind::True) {
610 return rhs_val;
611 }
612 }
613
614 if let Some(t) = manager.get(rhs_val)
615 && matches!(t.kind, TermKind::True)
616 {
617 return manager.mk_true();
618 }
619
620 manager.mk_implies(lhs_val, rhs_val)
621 }
622
623 TermKind::Ite(cond, then_br, else_br) => {
624 let cond_val = self.eval(cond, manager);
625
626 if let Some(t) = manager.get(cond_val) {
627 match t.kind {
628 TermKind::True => return self.eval(then_br, manager),
629 TermKind::False => return self.eval(else_br, manager),
630 _ => {}
631 }
632 }
633
634 let then_val = self.eval(then_br, manager);
635 let else_val = self.eval(else_br, manager);
636 manager.mk_ite(cond_val, then_val, else_val)
637 }
638
639 TermKind::Eq(lhs, rhs) => {
640 let lhs_val = self.eval(lhs, manager);
641 let rhs_val = self.eval(rhs, manager);
642
643 if lhs_val == rhs_val {
644 manager.mk_true()
645 } else {
646 manager.mk_eq(lhs_val, rhs_val)
647 }
648 }
649
650 TermKind::Neg(arg) => {
652 let arg_val = self.eval(arg, manager);
653 if let Some(t) = manager.get(arg_val) {
654 match &t.kind {
655 TermKind::IntConst(n) => return manager.mk_int(-n),
656 TermKind::RealConst(r) => return manager.mk_real(-r),
657 _ => {}
658 }
659 }
660 manager.mk_not(arg_val)
661 }
662
663 TermKind::Add(ref args) => {
664 let eval_args: Vec<_> = args.iter().map(|&a| self.eval(a, manager)).collect();
665 manager.mk_add(eval_args)
666 }
667
668 TermKind::Sub(lhs, rhs) => {
669 let lhs_val = self.eval(lhs, manager);
670 let rhs_val = self.eval(rhs, manager);
671 manager.mk_sub(lhs_val, rhs_val)
672 }
673
674 TermKind::Mul(ref args) => {
675 let eval_args: Vec<_> = args.iter().map(|&a| self.eval(a, manager)).collect();
676 manager.mk_mul(eval_args)
677 }
678
679 _ => self.get(term).unwrap_or(term),
681 }
682 }
683}
684
685impl Default for Model {
686 fn default() -> Self {
687 Self::new()
688 }
689}
690
691impl Model {
692 pub fn pretty_print(&self, manager: &TermManager) -> String {
694 if self.assignments.is_empty() {
695 return "(model)".to_string();
696 }
697
698 let mut lines = vec!["(model".to_string()];
699 let printer = oxiz_core::smtlib::Printer::new(manager);
700
701 for (&var, &value) in &self.assignments {
702 if let Some(term) = manager.get(var) {
703 if let TermKind::Var(name) = &term.kind {
705 let sort_str = Self::format_sort(term.sort, manager);
706 let value_str = printer.print_term(value);
707 let name_str = format!("{:?}", name);
709 lines.push(format!(
710 " (define-fun {} () {} {})",
711 name_str, sort_str, value_str
712 ));
713 }
714 }
715 }
716 lines.push(")".to_string());
717 lines.join("\n")
718 }
719
720 fn format_sort(sort: oxiz_core::sort::SortId, manager: &TermManager) -> String {
722 if sort == manager.sorts.bool_sort {
723 "Bool".to_string()
724 } else if sort == manager.sorts.int_sort {
725 "Int".to_string()
726 } else if sort == manager.sorts.real_sort {
727 "Real".to_string()
728 } else if let Some(s) = manager.sorts.get(sort) {
729 if let Some(w) = s.bitvec_width() {
730 format!("(_ BitVec {})", w)
731 } else {
732 "Unknown".to_string()
733 }
734 } else {
735 "Unknown".to_string()
736 }
737 }
738}
739
740#[derive(Debug, Clone)]
742pub struct NamedAssertion {
743 #[allow(dead_code)]
745 pub term: TermId,
746 pub name: Option<String>,
748 pub index: u32,
750}
751
752#[derive(Debug, Clone)]
754pub struct UnsatCore {
755 pub names: Vec<String>,
757 pub indices: Vec<u32>,
759}
760
761impl UnsatCore {
762 #[must_use]
764 pub fn new() -> Self {
765 Self {
766 names: Vec::new(),
767 indices: Vec::new(),
768 }
769 }
770
771 #[must_use]
773 pub fn is_empty(&self) -> bool {
774 self.indices.is_empty()
775 }
776
777 #[must_use]
779 pub fn len(&self) -> usize {
780 self.indices.len()
781 }
782}
783
784impl Default for UnsatCore {
785 fn default() -> Self {
786 Self::new()
787 }
788}
789
790#[derive(Debug)]
792pub struct Solver {
793 config: SolverConfig,
795 sat: SatSolver,
797 euf: EufSolver,
799 arith: ArithSolver,
801 bv: BvSolver,
803 mbqi: MBQISolver,
805 has_quantifiers: bool,
807 term_to_var: FxHashMap<TermId, Var>,
809 var_to_term: Vec<TermId>,
811 var_to_constraint: FxHashMap<Var, Constraint>,
813 var_to_parsed_arith: FxHashMap<Var, ParsedArithConstraint>,
815 logic: Option<String>,
817 assertions: Vec<TermId>,
819 named_assertions: Vec<NamedAssertion>,
821 #[allow(dead_code)]
824 assumption_vars: FxHashMap<u32, Var>,
825 model: Option<Model>,
827 unsat_core: Option<UnsatCore>,
829 context_stack: Vec<ContextState>,
831 trail: Vec<TrailOp>,
833 theory_processed_up_to: usize,
835 produce_unsat_cores: bool,
837 has_false_assertion: bool,
839 polarities: FxHashMap<TermId, Polarity>,
841 polarity_aware: bool,
843 theory_aware_branching: bool,
845 proof: Option<Proof>,
847 simplifier: Simplifier,
849 statistics: Statistics,
851 bv_terms: FxHashSet<TermId>,
853 arith_terms: FxHashSet<TermId>,
855}
856
857#[derive(Debug, Clone, Copy)]
859#[allow(dead_code)]
860pub struct TheoryDecision {
861 pub var: Var,
863 pub value: bool,
865 pub priority: i32,
867}
868
869struct TheoryManager<'a> {
871 euf: &'a mut EufSolver,
873 arith: &'a mut ArithSolver,
875 bv: &'a mut BvSolver,
877 var_to_constraint: &'a FxHashMap<Var, Constraint>,
879 var_to_parsed_arith: &'a FxHashMap<Var, ParsedArithConstraint>,
881 term_to_var: &'a FxHashMap<TermId, Var>,
883 level_stack: Vec<usize>,
885 processed_count: usize,
887 theory_mode: TheoryMode,
889 pending_assignments: Vec<(Lit, bool)>,
891 #[allow(dead_code)]
893 decision_hints: Vec<TheoryDecision>,
894 pending_equalities: Vec<EqualityNotification>,
896 processed_equalities: FxHashMap<(TermId, TermId), bool>,
898 statistics: &'a mut Statistics,
900 max_conflicts: u64,
902 #[allow(dead_code)]
904 max_decisions: u64,
905}
906
907impl<'a> TheoryManager<'a> {
908 #[allow(clippy::too_many_arguments)]
909 fn new(
910 euf: &'a mut EufSolver,
911 arith: &'a mut ArithSolver,
912 bv: &'a mut BvSolver,
913 var_to_constraint: &'a FxHashMap<Var, Constraint>,
914 var_to_parsed_arith: &'a FxHashMap<Var, ParsedArithConstraint>,
915 term_to_var: &'a FxHashMap<TermId, Var>,
916 theory_mode: TheoryMode,
917 statistics: &'a mut Statistics,
918 max_conflicts: u64,
919 max_decisions: u64,
920 ) -> Self {
921 Self {
922 euf,
923 arith,
924 bv,
925 var_to_constraint,
926 var_to_parsed_arith,
927 term_to_var,
928 level_stack: vec![0],
929 processed_count: 0,
930 theory_mode,
931 pending_assignments: Vec::new(),
932 decision_hints: Vec::new(),
933 pending_equalities: Vec::new(),
934 processed_equalities: FxHashMap::default(),
935 statistics,
936 max_conflicts,
937 max_decisions,
938 }
939 }
940
941 #[allow(dead_code)]
944 fn propagate_equalities(&mut self) -> TheoryCheckResult {
945 while let Some(eq) = self.pending_equalities.pop() {
947 let key = if eq.lhs < eq.rhs {
949 (eq.lhs, eq.rhs)
950 } else {
951 (eq.rhs, eq.lhs)
952 };
953
954 if self.processed_equalities.contains_key(&key) {
955 continue;
956 }
957 self.processed_equalities.insert(key, true);
958
959 let lhs_node = self.euf.intern(eq.lhs);
961 let rhs_node = self.euf.intern(eq.rhs);
962 if let Err(_e) = self
963 .euf
964 .merge(lhs_node, rhs_node, eq.reason.unwrap_or(eq.lhs))
965 {
966 continue;
968 }
969
970 if let Some(conflict_terms) = self.euf.check_conflicts() {
972 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
973 return TheoryCheckResult::Conflict(conflict_lits);
974 }
975
976 self.arith.notify_equality(eq);
978 }
979
980 TheoryCheckResult::Sat
981 }
982
983 #[allow(dead_code)]
987 fn model_based_combination(&mut self) -> TheoryCheckResult {
988 let mut shared_terms: Vec<TermId> = Vec::new();
990
991 for &term in self.term_to_var.keys() {
994 shared_terms.push(term);
995 }
996
997 if shared_terms.len() < 2 {
998 return TheoryCheckResult::Sat;
999 }
1000
1001 for i in 0..shared_terms.len() {
1004 for j in (i + 1)..shared_terms.len() {
1005 let t1 = shared_terms[i];
1006 let t2 = shared_terms[j];
1007
1008 let t1_node = self.euf.intern(t1);
1010 let t2_node = self.euf.intern(t2);
1011
1012 if self.euf.are_equal(t1_node, t2_node) {
1013 let t1_value = self.arith.value(t1);
1016 let t2_value = self.arith.value(t2);
1017
1018 if let (Some(v1), Some(v2)) = (t1_value, t2_value)
1019 && v1 != v2
1020 {
1021 let conflict_lits = self.terms_to_conflict_clause(&[t1, t2]);
1025 return TheoryCheckResult::Conflict(conflict_lits);
1026 }
1027 }
1028 }
1029 }
1030
1031 TheoryCheckResult::Sat
1032 }
1033
1034 #[allow(dead_code)]
1036 fn add_shared_equality(&mut self, lhs: TermId, rhs: TermId, reason: Option<TermId>) {
1037 self.pending_equalities
1038 .push(EqualityNotification { lhs, rhs, reason });
1039 }
1040
1041 #[allow(dead_code)]
1044 fn get_decision_hints(&mut self) -> &[TheoryDecision] {
1045 self.decision_hints.clear();
1047
1048 &self.decision_hints
1059 }
1060
1061 fn terms_to_conflict_clause(&self, terms: &[TermId]) -> SmallVec<[Lit; 8]> {
1064 let mut conflict = SmallVec::new();
1065 for &term in terms {
1066 if let Some(&var) = self.term_to_var.get(&term) {
1067 conflict.push(Lit::neg(var));
1069 }
1070 }
1071 conflict
1072 }
1073
1074 fn process_constraint(
1076 &mut self,
1077 var: Var,
1078 constraint: Constraint,
1079 is_positive: bool,
1080 ) -> TheoryCheckResult {
1081 match constraint {
1082 Constraint::Eq(lhs, rhs) => {
1083 if is_positive {
1084 let lhs_node = self.euf.intern(lhs);
1086 let rhs_node = self.euf.intern(rhs);
1087 if let Err(_e) = self.euf.merge(lhs_node, rhs_node, lhs) {
1088 return TheoryCheckResult::Sat;
1090 }
1091
1092 if let Some(conflict_terms) = self.euf.check_conflicts() {
1094 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1096 return TheoryCheckResult::Conflict(conflict_lits);
1097 }
1098
1099 if let Some(parsed) = self.var_to_parsed_arith.get(&var) {
1102 let terms: Vec<(TermId, Rational64)> =
1103 parsed.terms.iter().copied().collect();
1104 let constant = parsed.constant;
1105 let reason = parsed.reason_term;
1106
1107 self.arith.assert_le(&terms, constant, reason);
1109 self.arith.assert_ge(&terms, constant, reason);
1110
1111 use oxiz_theories::Theory;
1113 use oxiz_theories::TheoryCheckResult as TheoryCheckResultEnum;
1114 if let Ok(TheoryCheckResultEnum::Unsat(conflict_terms)) = self.arith.check()
1115 {
1116 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1117 return TheoryCheckResult::Conflict(conflict_lits);
1118 }
1119 }
1120 } else {
1121 let lhs_node = self.euf.intern(lhs);
1123 let rhs_node = self.euf.intern(rhs);
1124 self.euf.assert_diseq(lhs_node, rhs_node, lhs);
1125
1126 if let Some(conflict_terms) = self.euf.check_conflicts() {
1128 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1129 return TheoryCheckResult::Conflict(conflict_lits);
1130 }
1131 }
1132 }
1133 Constraint::Diseq(lhs, rhs) => {
1134 if is_positive {
1135 let lhs_node = self.euf.intern(lhs);
1137 let rhs_node = self.euf.intern(rhs);
1138 self.euf.assert_diseq(lhs_node, rhs_node, lhs);
1139
1140 if let Some(conflict_terms) = self.euf.check_conflicts() {
1141 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1142 return TheoryCheckResult::Conflict(conflict_lits);
1143 }
1144 } else {
1145 let lhs_node = self.euf.intern(lhs);
1147 let rhs_node = self.euf.intern(rhs);
1148 if let Err(_e) = self.euf.merge(lhs_node, rhs_node, lhs) {
1149 return TheoryCheckResult::Sat;
1150 }
1151
1152 if let Some(conflict_terms) = self.euf.check_conflicts() {
1153 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1154 return TheoryCheckResult::Conflict(conflict_lits);
1155 }
1156 }
1157 }
1158 Constraint::Lt(_lhs, _rhs)
1160 | Constraint::Le(_lhs, _rhs)
1161 | Constraint::Gt(_lhs, _rhs)
1162 | Constraint::Ge(_lhs, _rhs) => {
1163 if let Some(parsed) = self.var_to_parsed_arith.get(&var) {
1165 let terms: Vec<(TermId, Rational64)> = parsed.terms.iter().copied().collect();
1167 let reason = parsed.reason_term;
1168 let constant = parsed.constant;
1169
1170 if is_positive {
1171 match parsed.constraint_type {
1173 ArithConstraintType::Lt => {
1174 self.arith.assert_lt(&terms, constant, reason);
1176 }
1177 ArithConstraintType::Le => {
1178 self.arith.assert_le(&terms, constant, reason);
1180 }
1181 ArithConstraintType::Gt => {
1182 self.arith.assert_gt(&terms, constant, reason);
1184 }
1185 ArithConstraintType::Ge => {
1186 self.arith.assert_ge(&terms, constant, reason);
1188 }
1189 }
1190 } else {
1191 match parsed.constraint_type {
1197 ArithConstraintType::Lt => {
1198 self.arith.assert_ge(&terms, constant, reason);
1200 }
1201 ArithConstraintType::Le => {
1202 self.arith.assert_gt(&terms, constant, reason);
1204 }
1205 ArithConstraintType::Gt => {
1206 self.arith.assert_le(&terms, constant, reason);
1208 }
1209 ArithConstraintType::Ge => {
1210 self.arith.assert_lt(&terms, constant, reason);
1212 }
1213 }
1214 }
1215
1216 use oxiz_theories::Theory;
1218 use oxiz_theories::TheoryCheckResult as TheoryCheckResultEnum;
1219 if let Ok(TheoryCheckResultEnum::Unsat(conflict_terms)) = self.arith.check() {
1220 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1221 return TheoryCheckResult::Conflict(conflict_lits);
1222 }
1223 }
1224 }
1225 }
1226 TheoryCheckResult::Sat
1227 }
1228}
1229
1230impl TheoryCallback for TheoryManager<'_> {
1231 fn on_assignment(&mut self, lit: Lit) -> TheoryCheckResult {
1232 let var = lit.var();
1233 let is_positive = !lit.is_neg();
1234
1235 self.statistics.propagations += 1;
1237
1238 if self.theory_mode == TheoryMode::Lazy {
1240 if self.var_to_constraint.contains_key(&var) {
1242 self.pending_assignments.push((lit, is_positive));
1243 }
1244 return TheoryCheckResult::Sat;
1245 }
1246
1247 let Some(constraint) = self.var_to_constraint.get(&var).cloned() else {
1250 return TheoryCheckResult::Sat;
1251 };
1252
1253 self.processed_count += 1;
1254 self.statistics.theory_propagations += 1;
1255
1256 let result = self.process_constraint(var, constraint, is_positive);
1257
1258 if matches!(result, TheoryCheckResult::Conflict(_)) {
1260 self.statistics.theory_conflicts += 1;
1261 self.statistics.conflicts += 1;
1262
1263 if self.max_conflicts > 0 && self.statistics.conflicts >= self.max_conflicts {
1265 return TheoryCheckResult::Sat; }
1267 }
1268
1269 result
1270 }
1271
1272 fn final_check(&mut self) -> TheoryCheckResult {
1273 if self.theory_mode == TheoryMode::Lazy {
1275 for &(lit, is_positive) in &self.pending_assignments.clone() {
1276 let var = lit.var();
1277 let Some(constraint) = self.var_to_constraint.get(&var).cloned() else {
1278 continue;
1279 };
1280
1281 self.statistics.theory_propagations += 1;
1282
1283 let result = self.process_constraint(var, constraint, is_positive);
1285 if let TheoryCheckResult::Conflict(conflict) = result {
1286 self.statistics.theory_conflicts += 1;
1287 self.statistics.conflicts += 1;
1288
1289 if self.max_conflicts > 0 && self.statistics.conflicts >= self.max_conflicts {
1291 return TheoryCheckResult::Sat; }
1293
1294 return TheoryCheckResult::Conflict(conflict);
1295 }
1296 }
1297 self.pending_assignments.clear();
1299 }
1300
1301 if let Some(conflict_terms) = self.euf.check_conflicts() {
1303 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1305 self.statistics.theory_conflicts += 1;
1306 self.statistics.conflicts += 1;
1307
1308 if self.max_conflicts > 0 && self.statistics.conflicts >= self.max_conflicts {
1310 return TheoryCheckResult::Sat; }
1312
1313 return TheoryCheckResult::Conflict(conflict_lits);
1314 }
1315
1316 match self.arith.check() {
1318 Ok(result) => {
1319 match result {
1320 oxiz_theories::TheoryCheckResult::Sat => {
1321 self.model_based_combination()
1324 }
1325 oxiz_theories::TheoryCheckResult::Unsat(conflict_terms) => {
1326 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1328 self.statistics.theory_conflicts += 1;
1329 self.statistics.conflicts += 1;
1330
1331 if self.max_conflicts > 0 && self.statistics.conflicts >= self.max_conflicts
1333 {
1334 return TheoryCheckResult::Sat; }
1336
1337 TheoryCheckResult::Conflict(conflict_lits)
1338 }
1339 oxiz_theories::TheoryCheckResult::Propagate(_) => {
1340 self.model_based_combination()
1342 }
1343 oxiz_theories::TheoryCheckResult::Unknown => {
1344 TheoryCheckResult::Sat
1346 }
1347 }
1348 }
1349 Err(_error) => {
1350 TheoryCheckResult::Sat
1353 }
1354 }
1355 }
1356
1357 fn on_new_level(&mut self, level: u32) {
1358 while self.level_stack.len() < (level as usize + 1) {
1361 self.level_stack.push(self.processed_count);
1362 self.euf.push();
1363 self.arith.push();
1364 self.bv.push();
1365 }
1366 }
1367
1368 fn on_backtrack(&mut self, level: u32) {
1369 while self.level_stack.len() > (level as usize + 1) {
1371 self.level_stack.pop();
1372 self.euf.pop();
1373 self.arith.pop();
1374 self.bv.pop();
1375 }
1376 self.processed_count = *self.level_stack.last().unwrap_or(&0);
1377
1378 if self.theory_mode == TheoryMode::Lazy {
1380 self.pending_assignments.clear();
1381 }
1382 }
1383}
1384
1385#[derive(Debug, Clone)]
1387enum TrailOp {
1388 AssertionAdded { index: usize },
1390 VarCreated {
1392 #[allow(dead_code)]
1393 var: Var,
1394 term: TermId,
1395 },
1396 ConstraintAdded { var: Var },
1398 FalseAssertionSet,
1400 NamedAssertionAdded { index: usize },
1402 BvTermAdded { term: TermId },
1404 ArithTermAdded { term: TermId },
1406}
1407
1408#[derive(Debug, Clone)]
1410struct ContextState {
1411 num_assertions: usize,
1412 num_vars: usize,
1413 has_false_assertion: bool,
1414 trail_position: usize,
1416}
1417
1418impl Default for Solver {
1419 fn default() -> Self {
1420 Self::new()
1421 }
1422}
1423
1424impl Solver {
1425 #[must_use]
1427 pub fn new() -> Self {
1428 Self::with_config(SolverConfig::default())
1429 }
1430
1431 #[must_use]
1433 pub fn with_config(config: SolverConfig) -> Self {
1434 let proof_enabled = config.proof;
1435
1436 let sat_config = SatConfig {
1438 restart_strategy: config.restart_strategy,
1439 enable_inprocessing: config.enable_inprocessing,
1440 inprocessing_interval: config.inprocessing_interval,
1441 ..SatConfig::default()
1442 };
1443
1444 Self {
1454 config,
1455 sat: SatSolver::with_config(sat_config),
1456 euf: EufSolver::new(),
1457 arith: ArithSolver::lra(),
1458 bv: BvSolver::new(),
1459 mbqi: MBQISolver::new(),
1460 has_quantifiers: false,
1461 term_to_var: FxHashMap::default(),
1462 var_to_term: Vec::new(),
1463 var_to_constraint: FxHashMap::default(),
1464 var_to_parsed_arith: FxHashMap::default(),
1465 logic: None,
1466 assertions: Vec::new(),
1467 named_assertions: Vec::new(),
1468 assumption_vars: FxHashMap::default(),
1469 model: None,
1470 unsat_core: None,
1471 context_stack: Vec::new(),
1472 trail: Vec::new(),
1473 theory_processed_up_to: 0,
1474 produce_unsat_cores: false,
1475 has_false_assertion: false,
1476 polarities: FxHashMap::default(),
1477 polarity_aware: true, theory_aware_branching: true, proof: if proof_enabled {
1480 Some(Proof::new())
1481 } else {
1482 None
1483 },
1484 simplifier: Simplifier::new(),
1485 statistics: Statistics::new(),
1486 bv_terms: FxHashSet::default(),
1487 arith_terms: FxHashSet::default(),
1488 }
1489 }
1490
1491 #[must_use]
1493 pub fn get_proof(&self) -> Option<&Proof> {
1494 self.proof.as_ref()
1495 }
1496
1497 #[must_use]
1499 pub fn get_statistics(&self) -> &Statistics {
1500 &self.statistics
1501 }
1502
1503 pub fn reset_statistics(&mut self) {
1505 self.statistics.reset();
1506 }
1507
1508 pub fn set_theory_aware_branching(&mut self, enabled: bool) {
1510 self.theory_aware_branching = enabled;
1511 }
1512
1513 #[must_use]
1515 pub fn theory_aware_branching(&self) -> bool {
1516 self.theory_aware_branching
1517 }
1518
1519 pub fn set_produce_unsat_cores(&mut self, produce: bool) {
1521 self.produce_unsat_cores = produce;
1522 }
1523
1524 pub fn set_logic(&mut self, logic: &str) {
1526 self.logic = Some(logic.to_string());
1527
1528 if logic.contains("LIA") || logic.contains("IDL") || logic.contains("NIA") {
1530 self.arith = ArithSolver::lia();
1532 } else if logic.contains("LRA") || logic.contains("RDL") || logic.contains("NRA") {
1533 self.arith = ArithSolver::lra();
1535 } else if logic.contains("BV") {
1536 self.arith = ArithSolver::lia();
1539 }
1540 }
1542
1543 fn collect_polarities(&mut self, term: TermId, polarity: Polarity, manager: &TermManager) {
1546 let current = self.polarities.get(&term).copied();
1548 let new_polarity = match (current, polarity) {
1549 (Some(Polarity::Both), _) | (_, Polarity::Both) => Polarity::Both,
1550 (Some(Polarity::Positive), Polarity::Negative)
1551 | (Some(Polarity::Negative), Polarity::Positive) => Polarity::Both,
1552 (Some(p), _) => p,
1553 (None, p) => p,
1554 };
1555 self.polarities.insert(term, new_polarity);
1556
1557 if current == Some(Polarity::Both) {
1559 return;
1560 }
1561
1562 let Some(t) = manager.get(term) else {
1564 return;
1565 };
1566
1567 match &t.kind {
1568 TermKind::Not(arg) => {
1569 let neg_polarity = match polarity {
1570 Polarity::Positive => Polarity::Negative,
1571 Polarity::Negative => Polarity::Positive,
1572 Polarity::Both => Polarity::Both,
1573 };
1574 self.collect_polarities(*arg, neg_polarity, manager);
1575 }
1576 TermKind::And(args) | TermKind::Or(args) => {
1577 for &arg in args {
1578 self.collect_polarities(arg, polarity, manager);
1579 }
1580 }
1581 TermKind::Implies(lhs, rhs) => {
1582 let neg_polarity = match polarity {
1583 Polarity::Positive => Polarity::Negative,
1584 Polarity::Negative => Polarity::Positive,
1585 Polarity::Both => Polarity::Both,
1586 };
1587 self.collect_polarities(*lhs, neg_polarity, manager);
1588 self.collect_polarities(*rhs, polarity, manager);
1589 }
1590 TermKind::Ite(cond, then_br, else_br) => {
1591 self.collect_polarities(*cond, Polarity::Both, manager);
1592 self.collect_polarities(*then_br, polarity, manager);
1593 self.collect_polarities(*else_br, polarity, manager);
1594 }
1595 TermKind::Xor(lhs, rhs) | TermKind::Eq(lhs, rhs) => {
1596 self.collect_polarities(*lhs, Polarity::Both, manager);
1598 self.collect_polarities(*rhs, Polarity::Both, manager);
1599 }
1600 _ => {
1601 }
1603 }
1604 }
1605
1606 fn get_or_create_var(&mut self, term: TermId) -> Var {
1608 if let Some(&var) = self.term_to_var.get(&term) {
1609 return var;
1610 }
1611
1612 let var = self.sat.new_var();
1613 self.term_to_var.insert(term, var);
1614 self.trail.push(TrailOp::VarCreated { var, term });
1615
1616 while self.var_to_term.len() <= var.index() {
1617 self.var_to_term.push(TermId::new(0));
1618 }
1619 self.var_to_term[var.index()] = term;
1620 var
1621 }
1622
1623 fn track_theory_vars(&mut self, term_id: TermId, manager: &TermManager) {
1626 let Some(term) = manager.get(term_id) else {
1627 return;
1628 };
1629
1630 match &term.kind {
1631 TermKind::Var(_) => {
1632 let is_int = term.sort == manager.sorts.int_sort;
1634 let is_real = term.sort == manager.sorts.real_sort;
1635
1636 if is_int || is_real {
1637 if !self.arith_terms.contains(&term_id) {
1638 self.arith_terms.insert(term_id);
1639 self.trail.push(TrailOp::ArithTermAdded { term: term_id });
1640 self.arith.intern(term_id);
1641 }
1642 } else if let Some(sort) = manager.sorts.get(term.sort)
1643 && sort.is_bitvec()
1644 && !self.bv_terms.contains(&term_id)
1645 {
1646 self.bv_terms.insert(term_id);
1647 self.trail.push(TrailOp::BvTermAdded { term: term_id });
1648 if let Some(width) = sort.bitvec_width() {
1649 self.bv.new_bv(term_id, width);
1650 }
1651 self.arith.intern(term_id);
1654 }
1655 }
1656 TermKind::Add(args)
1658 | TermKind::Mul(args)
1659 | TermKind::And(args)
1660 | TermKind::Or(args) => {
1661 for &arg in args {
1662 self.track_theory_vars(arg, manager);
1663 }
1664 }
1665 TermKind::Sub(lhs, rhs)
1666 | TermKind::Eq(lhs, rhs)
1667 | TermKind::Lt(lhs, rhs)
1668 | TermKind::Le(lhs, rhs)
1669 | TermKind::Gt(lhs, rhs)
1670 | TermKind::Ge(lhs, rhs)
1671 | TermKind::BvAdd(lhs, rhs)
1672 | TermKind::BvSub(lhs, rhs)
1673 | TermKind::BvMul(lhs, rhs)
1674 | TermKind::BvUlt(lhs, rhs)
1675 | TermKind::BvUle(lhs, rhs)
1676 | TermKind::BvSlt(lhs, rhs)
1677 | TermKind::BvSle(lhs, rhs) => {
1678 self.track_theory_vars(*lhs, manager);
1679 self.track_theory_vars(*rhs, manager);
1680 }
1681 TermKind::Neg(arg) | TermKind::Not(arg) | TermKind::BvNot(arg) => {
1682 self.track_theory_vars(*arg, manager);
1683 }
1684 TermKind::Ite(cond, then_br, else_br) => {
1685 self.track_theory_vars(*cond, manager);
1686 self.track_theory_vars(*then_br, manager);
1687 self.track_theory_vars(*else_br, manager);
1688 }
1689 _ => {}
1691 }
1692 }
1693
1694 fn parse_arith_comparison(
1697 &self,
1698 lhs: TermId,
1699 rhs: TermId,
1700 constraint_type: ArithConstraintType,
1701 reason: TermId,
1702 manager: &TermManager,
1703 ) -> Option<ParsedArithConstraint> {
1704 let mut terms: SmallVec<[(TermId, Rational64); 4]> = SmallVec::new();
1705 let mut constant = Rational64::zero();
1706
1707 self.extract_linear_terms(lhs, Rational64::one(), &mut terms, &mut constant, manager)?;
1709
1710 self.extract_linear_terms(rhs, -Rational64::one(), &mut terms, &mut constant, manager)?;
1713
1714 let mut combined: FxHashMap<TermId, Rational64> = FxHashMap::default();
1716 for (term, coef) in terms {
1717 *combined.entry(term).or_insert(Rational64::zero()) += coef;
1718 }
1719
1720 let final_terms: SmallVec<[(TermId, Rational64); 4]> =
1722 combined.into_iter().filter(|(_, c)| !c.is_zero()).collect();
1723
1724 Some(ParsedArithConstraint {
1725 terms: final_terms,
1726 constant: -constant, constraint_type,
1728 reason_term: reason,
1729 })
1730 }
1731
1732 #[allow(clippy::only_used_in_recursion)]
1735 fn extract_linear_terms(
1736 &self,
1737 term_id: TermId,
1738 scale: Rational64,
1739 terms: &mut SmallVec<[(TermId, Rational64); 4]>,
1740 constant: &mut Rational64,
1741 manager: &TermManager,
1742 ) -> Option<()> {
1743 let term = manager.get(term_id)?;
1744
1745 match &term.kind {
1746 TermKind::IntConst(n) => {
1748 if let Some(val) = n.to_i64() {
1749 *constant += scale * Rational64::from_integer(val);
1750 Some(())
1751 } else {
1752 None
1754 }
1755 }
1756
1757 TermKind::RealConst(r) => {
1759 *constant += scale * *r;
1760 Some(())
1761 }
1762
1763 TermKind::BitVecConst { value, .. } => {
1765 if let Some(val) = value.to_i64() {
1766 *constant += scale * Rational64::from_integer(val);
1767 Some(())
1768 } else {
1769 None
1771 }
1772 }
1773
1774 TermKind::Var(_) => {
1776 terms.push((term_id, scale));
1777 Some(())
1778 }
1779
1780 TermKind::Add(args) => {
1782 for &arg in args {
1783 self.extract_linear_terms(arg, scale, terms, constant, manager)?;
1784 }
1785 Some(())
1786 }
1787
1788 TermKind::Sub(lhs, rhs) => {
1790 self.extract_linear_terms(*lhs, scale, terms, constant, manager)?;
1791 self.extract_linear_terms(*rhs, -scale, terms, constant, manager)?;
1792 Some(())
1793 }
1794
1795 TermKind::Neg(arg) => self.extract_linear_terms(*arg, -scale, terms, constant, manager),
1797
1798 TermKind::Mul(args) => {
1800 let mut const_product = Rational64::one();
1802 let mut var_term: Option<TermId> = None;
1803
1804 for &arg in args {
1805 let arg_term = manager.get(arg)?;
1806 match &arg_term.kind {
1807 TermKind::IntConst(n) => {
1808 if let Some(val) = n.to_i64() {
1809 const_product *= Rational64::from_integer(val);
1810 } else {
1811 return None; }
1813 }
1814 TermKind::RealConst(r) => {
1815 const_product *= *r;
1816 }
1817 _ => {
1818 if var_term.is_some() {
1819 return None;
1821 }
1822 var_term = Some(arg);
1823 }
1824 }
1825 }
1826
1827 let new_scale = scale * const_product;
1828 match var_term {
1829 Some(v) => self.extract_linear_terms(v, new_scale, terms, constant, manager),
1830 None => {
1831 *constant += new_scale;
1833 Some(())
1834 }
1835 }
1836 }
1837
1838 _ => None,
1840 }
1841 }
1842
1843 pub fn assert(&mut self, term: TermId, manager: &mut TermManager) {
1845 let index = self.assertions.len();
1846 self.assertions.push(term);
1847 self.trail.push(TrailOp::AssertionAdded { index });
1848
1849 if let Some(t) = manager.get(term) {
1851 match t.kind {
1852 TermKind::False => {
1853 if !self.has_false_assertion {
1855 self.has_false_assertion = true;
1856 self.trail.push(TrailOp::FalseAssertionSet);
1857 }
1858 if self.produce_unsat_cores {
1859 let na_index = self.named_assertions.len();
1860 self.named_assertions.push(NamedAssertion {
1861 term,
1862 name: None,
1863 index: index as u32,
1864 });
1865 self.trail
1866 .push(TrailOp::NamedAssertionAdded { index: na_index });
1867 }
1868 return;
1869 }
1870 TermKind::True => {
1871 if self.produce_unsat_cores {
1873 let na_index = self.named_assertions.len();
1874 self.named_assertions.push(NamedAssertion {
1875 term,
1876 name: None,
1877 index: index as u32,
1878 });
1879 self.trail
1880 .push(TrailOp::NamedAssertionAdded { index: na_index });
1881 }
1882 return;
1883 }
1884 _ => {}
1885 }
1886 }
1887
1888 let term_to_encode = if self.config.simplify {
1890 self.simplifier.simplify(term, manager)
1891 } else {
1892 term
1893 };
1894
1895 if let Some(t) = manager.get(term_to_encode) {
1897 match t.kind {
1898 TermKind::False => {
1899 if !self.has_false_assertion {
1900 self.has_false_assertion = true;
1901 self.trail.push(TrailOp::FalseAssertionSet);
1902 }
1903 return;
1904 }
1905 TermKind::True => {
1906 return;
1908 }
1909 _ => {}
1910 }
1911 }
1912
1913 if self.polarity_aware {
1915 self.collect_polarities(term_to_encode, Polarity::Positive, manager);
1916 }
1917
1918 let lit = self.encode(term_to_encode, manager);
1920 self.sat.add_clause([lit]);
1921
1922 if self.produce_unsat_cores {
1923 let na_index = self.named_assertions.len();
1924 self.named_assertions.push(NamedAssertion {
1925 term,
1926 name: None,
1927 index: index as u32,
1928 });
1929 self.trail
1930 .push(TrailOp::NamedAssertionAdded { index: na_index });
1931 }
1932 }
1933
1934 pub fn assert_named(&mut self, term: TermId, name: &str, manager: &mut TermManager) {
1936 let index = self.assertions.len();
1937 self.assertions.push(term);
1938 self.trail.push(TrailOp::AssertionAdded { index });
1939
1940 if let Some(t) = manager.get(term) {
1942 match t.kind {
1943 TermKind::False => {
1944 if !self.has_false_assertion {
1946 self.has_false_assertion = true;
1947 self.trail.push(TrailOp::FalseAssertionSet);
1948 }
1949 if self.produce_unsat_cores {
1950 let na_index = self.named_assertions.len();
1951 self.named_assertions.push(NamedAssertion {
1952 term,
1953 name: Some(name.to_string()),
1954 index: index as u32,
1955 });
1956 self.trail
1957 .push(TrailOp::NamedAssertionAdded { index: na_index });
1958 }
1959 return;
1960 }
1961 TermKind::True => {
1962 if self.produce_unsat_cores {
1964 let na_index = self.named_assertions.len();
1965 self.named_assertions.push(NamedAssertion {
1966 term,
1967 name: Some(name.to_string()),
1968 index: index as u32,
1969 });
1970 self.trail
1971 .push(TrailOp::NamedAssertionAdded { index: na_index });
1972 }
1973 return;
1974 }
1975 _ => {}
1976 }
1977 }
1978
1979 if self.polarity_aware {
1981 self.collect_polarities(term, Polarity::Positive, manager);
1982 }
1983
1984 let lit = self.encode(term, manager);
1986 self.sat.add_clause([lit]);
1987
1988 if self.produce_unsat_cores {
1989 let na_index = self.named_assertions.len();
1990 self.named_assertions.push(NamedAssertion {
1991 term,
1992 name: Some(name.to_string()),
1993 index: index as u32,
1994 });
1995 self.trail
1996 .push(TrailOp::NamedAssertionAdded { index: na_index });
1997 }
1998 }
1999
2000 #[must_use]
2002 pub fn get_unsat_core(&self) -> Option<&UnsatCore> {
2003 self.unsat_core.as_ref()
2004 }
2005
2006 fn encode(&mut self, term: TermId, manager: &mut TermManager) -> Lit {
2008 let Some(t) = manager.get(term).cloned() else {
2010 let var = self.get_or_create_var(term);
2011 return Lit::pos(var);
2012 };
2013
2014 match &t.kind {
2015 TermKind::True => {
2016 let var = self.get_or_create_var(manager.mk_true());
2017 self.sat.add_clause([Lit::pos(var)]);
2018 Lit::pos(var)
2019 }
2020 TermKind::False => {
2021 let var = self.get_or_create_var(manager.mk_false());
2022 self.sat.add_clause([Lit::neg(var)]);
2023 Lit::neg(var)
2024 }
2025 TermKind::Var(_) => {
2026 let var = self.get_or_create_var(term);
2027 let is_int = t.sort == manager.sorts.int_sort;
2029 let is_real = t.sort == manager.sorts.real_sort;
2030
2031 if is_int || is_real {
2032 if !self.arith_terms.contains(&term) {
2034 self.arith_terms.insert(term);
2035 self.trail.push(TrailOp::ArithTermAdded { term });
2036 self.arith.intern(term);
2038 }
2039 } else if let Some(sort) = manager.sorts.get(t.sort)
2040 && sort.is_bitvec()
2041 && !self.bv_terms.contains(&term)
2042 {
2043 self.bv_terms.insert(term);
2044 self.trail.push(TrailOp::BvTermAdded { term });
2045 if let Some(width) = sort.bitvec_width() {
2047 self.bv.new_bv(term, width);
2048 }
2049 }
2050 Lit::pos(var)
2051 }
2052 TermKind::Not(arg) => {
2053 let arg_lit = self.encode(*arg, manager);
2054 arg_lit.negate()
2055 }
2056 TermKind::And(args) => {
2057 let result_var = self.get_or_create_var(term);
2058 let result = Lit::pos(result_var);
2059
2060 let mut arg_lits: Vec<Lit> = Vec::new();
2061 for &arg in args {
2062 arg_lits.push(self.encode(arg, manager));
2063 }
2064
2065 let polarity = if self.polarity_aware {
2067 self.polarities
2068 .get(&term)
2069 .copied()
2070 .unwrap_or(Polarity::Both)
2071 } else {
2072 Polarity::Both
2073 };
2074
2075 if polarity != Polarity::Negative {
2078 for &arg in &arg_lits {
2079 self.sat.add_clause([result.negate(), arg]);
2080 }
2081 }
2082
2083 if polarity != Polarity::Positive {
2086 let mut clause: Vec<Lit> = arg_lits.iter().map(|l| l.negate()).collect();
2087 clause.push(result);
2088 self.sat.add_clause(clause);
2089 }
2090
2091 result
2092 }
2093 TermKind::Or(args) => {
2094 let result_var = self.get_or_create_var(term);
2095 let result = Lit::pos(result_var);
2096
2097 let mut arg_lits: Vec<Lit> = Vec::new();
2098 for &arg in args {
2099 arg_lits.push(self.encode(arg, manager));
2100 }
2101
2102 let polarity = if self.polarity_aware {
2104 self.polarities
2105 .get(&term)
2106 .copied()
2107 .unwrap_or(Polarity::Both)
2108 } else {
2109 Polarity::Both
2110 };
2111
2112 if polarity != Polarity::Negative {
2115 let mut clause: Vec<Lit> = vec![result.negate()];
2116 clause.extend(arg_lits.iter().copied());
2117 self.sat.add_clause(clause);
2118 }
2119
2120 if polarity != Polarity::Positive {
2123 for &arg in &arg_lits {
2124 self.sat.add_clause([arg.negate(), result]);
2125 }
2126 }
2127
2128 result
2129 }
2130 TermKind::Xor(lhs, rhs) => {
2131 let lhs_lit = self.encode(*lhs, manager);
2132 let rhs_lit = self.encode(*rhs, manager);
2133
2134 let result_var = self.get_or_create_var(term);
2135 let result = Lit::pos(result_var);
2136
2137 self.sat.add_clause([result.negate(), lhs_lit, rhs_lit]);
2142 self.sat
2144 .add_clause([result.negate(), lhs_lit.negate(), rhs_lit.negate()]);
2145
2146 self.sat.add_clause([lhs_lit.negate(), rhs_lit, result]);
2148 self.sat.add_clause([lhs_lit, rhs_lit.negate(), result]);
2150
2151 result
2152 }
2153 TermKind::Implies(lhs, rhs) => {
2154 let lhs_lit = self.encode(*lhs, manager);
2155 let rhs_lit = self.encode(*rhs, manager);
2156
2157 let result_var = self.get_or_create_var(term);
2158 let result = Lit::pos(result_var);
2159
2160 self.sat
2163 .add_clause([result.negate(), lhs_lit.negate(), rhs_lit]);
2164
2165 self.sat.add_clause([lhs_lit, result]);
2168 self.sat.add_clause([rhs_lit.negate(), result]);
2169
2170 result
2171 }
2172 TermKind::Ite(cond, then_br, else_br) => {
2173 let cond_lit = self.encode(*cond, manager);
2174 let then_lit = self.encode(*then_br, manager);
2175 let else_lit = self.encode(*else_br, manager);
2176
2177 let result_var = self.get_or_create_var(term);
2178 let result = Lit::pos(result_var);
2179
2180 self.sat
2183 .add_clause([cond_lit.negate(), result.negate(), then_lit]);
2184 self.sat
2186 .add_clause([cond_lit.negate(), then_lit.negate(), result]);
2187
2188 self.sat.add_clause([cond_lit, result.negate(), else_lit]);
2190 self.sat.add_clause([cond_lit, else_lit.negate(), result]);
2192
2193 result
2194 }
2195 TermKind::Eq(lhs, rhs) => {
2196 let lhs_term = manager.get(*lhs);
2198 let is_bool_eq = lhs_term.is_some_and(|t| t.sort == manager.sorts.bool_sort);
2199
2200 if is_bool_eq {
2201 let lhs_lit = self.encode(*lhs, manager);
2203 let rhs_lit = self.encode(*rhs, manager);
2204
2205 let result_var = self.get_or_create_var(term);
2206 let result = Lit::pos(result_var);
2207
2208 self.sat
2211 .add_clause([result.negate(), lhs_lit.negate(), rhs_lit]);
2212 self.sat
2213 .add_clause([result.negate(), rhs_lit.negate(), lhs_lit]);
2214
2215 self.sat.add_clause([lhs_lit, rhs_lit, result]);
2217 self.sat
2218 .add_clause([lhs_lit.negate(), rhs_lit.negate(), result]);
2219
2220 result
2221 } else {
2222 let var = self.get_or_create_var(term);
2225 self.var_to_constraint
2226 .insert(var, Constraint::Eq(*lhs, *rhs));
2227 self.trail.push(TrailOp::ConstraintAdded { var });
2228
2229 self.track_theory_vars(*lhs, manager);
2231 self.track_theory_vars(*rhs, manager);
2232
2233 let is_arith = lhs_term.is_some_and(|t| {
2236 t.sort == manager.sorts.int_sort || t.sort == manager.sorts.real_sort
2237 });
2238 if is_arith {
2239 if let Some(parsed) = self.parse_arith_comparison(
2242 *lhs,
2243 *rhs,
2244 ArithConstraintType::Le,
2245 term,
2246 manager,
2247 ) {
2248 self.var_to_parsed_arith.insert(var, parsed);
2249 }
2250 }
2251
2252 Lit::pos(var)
2253 }
2254 }
2255 TermKind::Distinct(args) => {
2256 if args.len() <= 1 {
2259 let var = self.get_or_create_var(manager.mk_true());
2261 return Lit::pos(var);
2262 }
2263
2264 let result_var = self.get_or_create_var(term);
2265 let result = Lit::pos(result_var);
2266
2267 let mut diseq_lits = Vec::new();
2268 for i in 0..args.len() {
2269 for j in (i + 1)..args.len() {
2270 let eq = manager.mk_eq(args[i], args[j]);
2271 let eq_lit = self.encode(eq, manager);
2272 diseq_lits.push(eq_lit.negate());
2273 }
2274 }
2275
2276 for &diseq in &diseq_lits {
2278 self.sat.add_clause([result.negate(), diseq]);
2279 }
2280
2281 let mut clause: Vec<Lit> = diseq_lits.iter().map(|l| l.negate()).collect();
2283 clause.push(result);
2284 self.sat.add_clause(clause);
2285
2286 result
2287 }
2288 TermKind::Let { bindings, body } => {
2289 let substituted = *body;
2293 for (name, value) in bindings.iter().rev() {
2294 let _ = (name, value);
2297 }
2298 self.encode(substituted, manager)
2299 }
2300 TermKind::IntConst(_) | TermKind::RealConst(_) | TermKind::BitVecConst { .. } => {
2303 let var = self.get_or_create_var(term);
2306 Lit::pos(var)
2307 }
2308 TermKind::Neg(_)
2309 | TermKind::Add(_)
2310 | TermKind::Sub(_, _)
2311 | TermKind::Mul(_)
2312 | TermKind::Div(_, _)
2313 | TermKind::Mod(_, _) => {
2314 let var = self.get_or_create_var(term);
2316 Lit::pos(var)
2317 }
2318 TermKind::Lt(lhs, rhs) => {
2319 let var = self.get_or_create_var(term);
2321 self.var_to_constraint
2322 .insert(var, Constraint::Lt(*lhs, *rhs));
2323 self.trail.push(TrailOp::ConstraintAdded { var });
2324 if let Some(parsed) =
2326 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Lt, term, manager)
2327 {
2328 self.var_to_parsed_arith.insert(var, parsed);
2329 }
2330 self.track_theory_vars(*lhs, manager);
2332 self.track_theory_vars(*rhs, manager);
2333 Lit::pos(var)
2334 }
2335 TermKind::Le(lhs, rhs) => {
2336 let var = self.get_or_create_var(term);
2338 self.var_to_constraint
2339 .insert(var, Constraint::Le(*lhs, *rhs));
2340 self.trail.push(TrailOp::ConstraintAdded { var });
2341 if let Some(parsed) =
2343 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Le, term, manager)
2344 {
2345 self.var_to_parsed_arith.insert(var, parsed);
2346 }
2347 self.track_theory_vars(*lhs, manager);
2349 self.track_theory_vars(*rhs, manager);
2350 Lit::pos(var)
2351 }
2352 TermKind::Gt(lhs, rhs) => {
2353 let var = self.get_or_create_var(term);
2355 self.var_to_constraint
2356 .insert(var, Constraint::Gt(*lhs, *rhs));
2357 self.trail.push(TrailOp::ConstraintAdded { var });
2358 if let Some(parsed) =
2360 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Gt, term, manager)
2361 {
2362 self.var_to_parsed_arith.insert(var, parsed);
2363 }
2364 self.track_theory_vars(*lhs, manager);
2366 self.track_theory_vars(*rhs, manager);
2367 Lit::pos(var)
2368 }
2369 TermKind::Ge(lhs, rhs) => {
2370 let var = self.get_or_create_var(term);
2372 self.var_to_constraint
2373 .insert(var, Constraint::Ge(*lhs, *rhs));
2374 self.trail.push(TrailOp::ConstraintAdded { var });
2375 if let Some(parsed) =
2377 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Ge, term, manager)
2378 {
2379 self.var_to_parsed_arith.insert(var, parsed);
2380 }
2381 self.track_theory_vars(*lhs, manager);
2383 self.track_theory_vars(*rhs, manager);
2384 Lit::pos(var)
2385 }
2386 TermKind::BvConcat(_, _)
2387 | TermKind::BvExtract { .. }
2388 | TermKind::BvNot(_)
2389 | TermKind::BvAnd(_, _)
2390 | TermKind::BvOr(_, _)
2391 | TermKind::BvXor(_, _)
2392 | TermKind::BvAdd(_, _)
2393 | TermKind::BvSub(_, _)
2394 | TermKind::BvMul(_, _)
2395 | TermKind::BvUdiv(_, _)
2396 | TermKind::BvSdiv(_, _)
2397 | TermKind::BvUrem(_, _)
2398 | TermKind::BvSrem(_, _)
2399 | TermKind::BvShl(_, _)
2400 | TermKind::BvLshr(_, _)
2401 | TermKind::BvAshr(_, _) => {
2402 let var = self.get_or_create_var(term);
2404 Lit::pos(var)
2405 }
2406 TermKind::BvUlt(lhs, rhs) => {
2407 let var = self.get_or_create_var(term);
2409 self.var_to_constraint
2410 .insert(var, Constraint::Lt(*lhs, *rhs));
2411 self.trail.push(TrailOp::ConstraintAdded { var });
2412 if let Some(parsed) =
2414 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Lt, term, manager)
2415 {
2416 self.var_to_parsed_arith.insert(var, parsed);
2417 }
2418 self.track_theory_vars(*lhs, manager);
2420 self.track_theory_vars(*rhs, manager);
2421 Lit::pos(var)
2422 }
2423 TermKind::BvUle(lhs, rhs) => {
2424 let var = self.get_or_create_var(term);
2426 self.var_to_constraint
2427 .insert(var, Constraint::Le(*lhs, *rhs));
2428 self.trail.push(TrailOp::ConstraintAdded { var });
2429 if let Some(parsed) =
2430 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Le, term, manager)
2431 {
2432 self.var_to_parsed_arith.insert(var, parsed);
2433 }
2434 self.track_theory_vars(*lhs, manager);
2436 self.track_theory_vars(*rhs, manager);
2437 Lit::pos(var)
2438 }
2439 TermKind::BvSlt(lhs, rhs) => {
2440 let var = self.get_or_create_var(term);
2442 self.var_to_constraint
2443 .insert(var, Constraint::Lt(*lhs, *rhs));
2444 self.trail.push(TrailOp::ConstraintAdded { var });
2445 if let Some(parsed) =
2446 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Lt, term, manager)
2447 {
2448 self.var_to_parsed_arith.insert(var, parsed);
2449 }
2450 self.track_theory_vars(*lhs, manager);
2452 self.track_theory_vars(*rhs, manager);
2453 Lit::pos(var)
2454 }
2455 TermKind::BvSle(lhs, rhs) => {
2456 let var = self.get_or_create_var(term);
2458 self.var_to_constraint
2459 .insert(var, Constraint::Le(*lhs, *rhs));
2460 self.trail.push(TrailOp::ConstraintAdded { var });
2461 if let Some(parsed) =
2462 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Le, term, manager)
2463 {
2464 self.var_to_parsed_arith.insert(var, parsed);
2465 }
2466 self.track_theory_vars(*lhs, manager);
2468 self.track_theory_vars(*rhs, manager);
2469 Lit::pos(var)
2470 }
2471 TermKind::Select(_, _) | TermKind::Store(_, _, _) => {
2472 let var = self.get_or_create_var(term);
2474 Lit::pos(var)
2475 }
2476 TermKind::Apply { .. } => {
2477 let var = self.get_or_create_var(term);
2479 Lit::pos(var)
2480 }
2481 TermKind::Forall { patterns, .. } => {
2482 self.has_quantifiers = true;
2484 self.mbqi.add_quantifier(term, manager);
2485 for pattern in patterns {
2487 for &trigger in pattern {
2488 self.mbqi.collect_ground_terms(trigger, manager);
2489 }
2490 }
2491 let var = self.get_or_create_var(term);
2493 Lit::pos(var)
2494 }
2495 TermKind::Exists { patterns, .. } => {
2496 self.has_quantifiers = true;
2498 self.mbqi.add_quantifier(term, manager);
2499 for pattern in patterns {
2501 for &trigger in pattern {
2502 self.mbqi.collect_ground_terms(trigger, manager);
2503 }
2504 }
2505 let var = self.get_or_create_var(term);
2507 Lit::pos(var)
2508 }
2509 TermKind::StringLit(_)
2511 | TermKind::StrConcat(_, _)
2512 | TermKind::StrLen(_)
2513 | TermKind::StrSubstr(_, _, _)
2514 | TermKind::StrAt(_, _)
2515 | TermKind::StrReplace(_, _, _)
2516 | TermKind::StrReplaceAll(_, _, _)
2517 | TermKind::StrToInt(_)
2518 | TermKind::IntToStr(_)
2519 | TermKind::StrInRe(_, _) => {
2520 let var = self.get_or_create_var(term);
2522 Lit::pos(var)
2523 }
2524 TermKind::StrContains(_, _)
2525 | TermKind::StrPrefixOf(_, _)
2526 | TermKind::StrSuffixOf(_, _)
2527 | TermKind::StrIndexOf(_, _, _) => {
2528 let var = self.get_or_create_var(term);
2530 Lit::pos(var)
2531 }
2532 TermKind::FpLit { .. }
2534 | TermKind::FpPlusInfinity { .. }
2535 | TermKind::FpMinusInfinity { .. }
2536 | TermKind::FpPlusZero { .. }
2537 | TermKind::FpMinusZero { .. }
2538 | TermKind::FpNaN { .. } => {
2539 let var = self.get_or_create_var(term);
2541 Lit::pos(var)
2542 }
2543 TermKind::FpAbs(_)
2545 | TermKind::FpNeg(_)
2546 | TermKind::FpSqrt(_, _)
2547 | TermKind::FpRoundToIntegral(_, _)
2548 | TermKind::FpAdd(_, _, _)
2549 | TermKind::FpSub(_, _, _)
2550 | TermKind::FpMul(_, _, _)
2551 | TermKind::FpDiv(_, _, _)
2552 | TermKind::FpRem(_, _)
2553 | TermKind::FpMin(_, _)
2554 | TermKind::FpMax(_, _)
2555 | TermKind::FpFma(_, _, _, _) => {
2556 let var = self.get_or_create_var(term);
2558 Lit::pos(var)
2559 }
2560 TermKind::FpLeq(_, _)
2562 | TermKind::FpLt(_, _)
2563 | TermKind::FpGeq(_, _)
2564 | TermKind::FpGt(_, _)
2565 | TermKind::FpEq(_, _)
2566 | TermKind::FpIsNormal(_)
2567 | TermKind::FpIsSubnormal(_)
2568 | TermKind::FpIsZero(_)
2569 | TermKind::FpIsInfinite(_)
2570 | TermKind::FpIsNaN(_)
2571 | TermKind::FpIsNegative(_)
2572 | TermKind::FpIsPositive(_) => {
2573 let var = self.get_or_create_var(term);
2575 Lit::pos(var)
2576 }
2577 TermKind::FpToFp { .. }
2579 | TermKind::FpToSBV { .. }
2580 | TermKind::FpToUBV { .. }
2581 | TermKind::FpToReal(_)
2582 | TermKind::RealToFp { .. }
2583 | TermKind::SBVToFp { .. }
2584 | TermKind::UBVToFp { .. } => {
2585 let var = self.get_or_create_var(term);
2587 Lit::pos(var)
2588 }
2589 TermKind::DtConstructor { .. }
2591 | TermKind::DtTester { .. }
2592 | TermKind::DtSelector { .. } => {
2593 let var = self.get_or_create_var(term);
2595 Lit::pos(var)
2596 }
2597 TermKind::Match { .. } => {
2599 let var = self.get_or_create_var(term);
2601 Lit::pos(var)
2602 }
2603 }
2604 }
2605
2606 pub fn check(&mut self, manager: &mut TermManager) -> SolverResult {
2608 if self.has_false_assertion {
2610 self.build_unsat_core_trivial_false();
2611 return SolverResult::Unsat;
2612 }
2613
2614 if self.assertions.is_empty() {
2615 return SolverResult::Sat;
2616 }
2617
2618 if self.config.max_conflicts > 0 && self.statistics.conflicts >= self.config.max_conflicts {
2620 return SolverResult::Unknown;
2621 }
2622 if self.config.max_decisions > 0 && self.statistics.decisions >= self.config.max_decisions {
2623 return SolverResult::Unknown;
2624 }
2625
2626 let mut theory_manager = TheoryManager::new(
2628 &mut self.euf,
2629 &mut self.arith,
2630 &mut self.bv,
2631 &self.var_to_constraint,
2632 &self.var_to_parsed_arith,
2633 &self.term_to_var,
2634 self.config.theory_mode,
2635 &mut self.statistics,
2636 self.config.max_conflicts,
2637 self.config.max_decisions,
2638 );
2639
2640 let max_mbqi_iterations = 100;
2642 let mut mbqi_iteration = 0;
2643
2644 loop {
2645 let sat_result = self.sat.solve_with_theory(&mut theory_manager);
2646
2647 match sat_result {
2648 SatResult::Unsat => {
2649 self.build_unsat_core();
2650 return SolverResult::Unsat;
2651 }
2652 SatResult::Unknown => {
2653 return SolverResult::Unknown;
2654 }
2655 SatResult::Sat => {
2656 if !self.has_quantifiers {
2658 self.build_model(manager);
2659 self.unsat_core = None;
2660 return SolverResult::Sat;
2661 }
2662
2663 self.build_model(manager);
2665
2666 let model_assignments = self
2668 .model
2669 .as_ref()
2670 .map(|m| m.assignments().clone())
2671 .unwrap_or_default();
2672 let mbqi_result = self.mbqi.check_with_model(&model_assignments, manager);
2673
2674 match mbqi_result {
2675 MBQIResult::NoQuantifiers | MBQIResult::Satisfied => {
2676 self.unsat_core = None;
2678 return SolverResult::Sat;
2679 }
2680 MBQIResult::InstantiationLimit => {
2681 return SolverResult::Unknown;
2683 }
2684 MBQIResult::Conflict(conflict_terms) => {
2685 let lits: Vec<Lit> = conflict_terms
2687 .iter()
2688 .filter_map(|&t| self.term_to_var.get(&t).map(|&v| Lit::neg(v)))
2689 .collect();
2690 if !lits.is_empty() {
2691 self.sat.add_clause(lits);
2692 }
2693 }
2695 MBQIResult::NewInstantiations(instantiations) => {
2696 for inst in instantiations {
2698 let lit = self.encode(inst.result, manager);
2701 self.sat.add_clause([lit]);
2702 }
2703 }
2705 }
2706
2707 mbqi_iteration += 1;
2708 if mbqi_iteration >= max_mbqi_iterations {
2709 return SolverResult::Unknown;
2710 }
2711
2712 theory_manager = TheoryManager::new(
2714 &mut self.euf,
2715 &mut self.arith,
2716 &mut self.bv,
2717 &self.var_to_constraint,
2718 &self.var_to_parsed_arith,
2719 &self.term_to_var,
2720 self.config.theory_mode,
2721 &mut self.statistics,
2722 self.config.max_conflicts,
2723 self.config.max_decisions,
2724 );
2725 }
2726 }
2727 }
2728 }
2729
2730 pub fn check_with_assumptions(
2733 &mut self,
2734 assumptions: &[TermId],
2735 manager: &mut TermManager,
2736 ) -> SolverResult {
2737 self.push();
2739
2740 for &assumption in assumptions {
2742 self.assert(assumption, manager);
2743 }
2744
2745 let result = self.check(manager);
2747
2748 self.pop();
2750
2751 result
2752 }
2753
2754 pub fn check_sat_only(&mut self, manager: &mut TermManager) -> SolverResult {
2757 if self.assertions.is_empty() {
2758 return SolverResult::Sat;
2759 }
2760
2761 match self.sat.solve() {
2762 SatResult::Sat => {
2763 self.build_model(manager);
2764 SolverResult::Sat
2765 }
2766 SatResult::Unsat => SolverResult::Unsat,
2767 SatResult::Unknown => SolverResult::Unknown,
2768 }
2769 }
2770
2771 fn build_model(&mut self, manager: &mut TermManager) {
2773 let mut model = Model::new();
2774 let sat_model = self.sat.model();
2775
2776 for (&term, &var) in &self.term_to_var {
2778 let val = sat_model.get(var.index()).copied();
2779 if let Some(v) = val {
2780 let bool_val = if v.is_true() {
2781 manager.mk_true()
2782 } else if v.is_false() {
2783 manager.mk_false()
2784 } else {
2785 continue;
2786 };
2787 model.set(term, bool_val);
2788 }
2789 }
2790
2791 for (&var, constraint) in &self.var_to_constraint {
2794 let is_true = sat_model
2796 .get(var.index())
2797 .copied()
2798 .is_some_and(|v| v.is_true());
2799
2800 if !is_true {
2801 continue;
2802 }
2803
2804 if let Constraint::Eq(lhs, rhs) = constraint {
2805 let (var_term, const_term) =
2807 if self.arith_terms.contains(lhs) || self.bv_terms.contains(lhs) {
2808 (*lhs, *rhs)
2809 } else if self.arith_terms.contains(rhs) || self.bv_terms.contains(rhs) {
2810 (*rhs, *lhs)
2811 } else {
2812 continue;
2813 };
2814
2815 let Some(const_term_data) = manager.get(const_term) else {
2817 continue;
2818 };
2819
2820 match &const_term_data.kind {
2821 TermKind::IntConst(n) => {
2822 if let Some(val) = n.to_i64() {
2823 let value_term = manager.mk_int(val);
2824 model.set(var_term, value_term);
2825 }
2826 }
2827 TermKind::RealConst(r) => {
2828 let value_term = manager.mk_real(*r);
2829 model.set(var_term, value_term);
2830 }
2831 TermKind::BitVecConst { value, width } => {
2832 if let Some(val) = value.to_u64() {
2833 let value_term = manager.mk_bitvec(val, *width);
2834 model.set(var_term, value_term);
2835 }
2836 }
2837 _ => {}
2838 }
2839 }
2840 }
2841
2842 for &term in &self.arith_terms {
2845 if model.get(term).is_some() {
2847 continue;
2848 }
2849
2850 if let Some(value) = self.arith.value(term) {
2851 let value_term = if *value.denom() == 1 {
2853 manager.mk_int(*value.numer())
2855 } else {
2856 manager.mk_real(value)
2858 };
2859 model.set(term, value_term);
2860 } else {
2861 let is_int = manager
2864 .get(term)
2865 .map(|t| t.sort == manager.sorts.int_sort)
2866 .unwrap_or(true);
2867
2868 let value_term = if is_int {
2869 manager.mk_int(0i64)
2870 } else {
2871 manager.mk_real(num_rational::Rational64::from_integer(0))
2872 };
2873 model.set(term, value_term);
2874 }
2875 }
2876
2877 for &term in &self.bv_terms {
2880 if model.get(term).is_some() {
2882 continue;
2883 }
2884
2885 let width = manager
2887 .get(term)
2888 .and_then(|t| manager.sorts.get(t.sort))
2889 .and_then(|s| s.bitvec_width())
2890 .unwrap_or(64);
2891
2892 if let Some(arith_value) = self.arith.value(term) {
2895 let int_value = arith_value.to_integer();
2896 let value_term = manager.mk_bitvec(int_value, width);
2897 model.set(term, value_term);
2898 } else if let Some(bv_value) = self.bv.get_value(term) {
2899 let value_term = manager.mk_bitvec(bv_value, width);
2901 model.set(term, value_term);
2902 } else {
2903 let value_term = manager.mk_bitvec(0i64, width);
2906 model.set(term, value_term);
2907 }
2908 }
2909
2910 self.model = Some(model);
2911 }
2912
2913 fn build_unsat_core_trivial_false(&mut self) {
2915 if !self.produce_unsat_cores {
2916 self.unsat_core = None;
2917 return;
2918 }
2919
2920 let mut core = UnsatCore::new();
2922
2923 for (i, &term) in self.assertions.iter().enumerate() {
2924 if term == TermId::new(1) {
2925 core.indices.push(i as u32);
2927
2928 if let Some(named) = self.named_assertions.iter().find(|na| na.index == i as u32)
2930 && let Some(ref name) = named.name
2931 {
2932 core.names.push(name.clone());
2933 }
2934 }
2935 }
2936
2937 self.unsat_core = Some(core);
2938 }
2939
2940 fn build_unsat_core(&mut self) {
2942 if !self.produce_unsat_cores {
2943 self.unsat_core = None;
2944 return;
2945 }
2946
2947 let mut core = UnsatCore::new();
2952
2953 if !self.assumption_vars.is_empty() {
2955 for na in &self.named_assertions {
2960 core.indices.push(na.index);
2961 if let Some(ref name) = na.name {
2962 core.names.push(name.clone());
2963 }
2964 }
2965 } else {
2966 for na in &self.named_assertions {
2969 core.indices.push(na.index);
2970 if let Some(ref name) = na.name {
2971 core.names.push(name.clone());
2972 }
2973 }
2974 }
2975
2976 self.unsat_core = Some(core);
2977 }
2978
2979 pub fn enable_assumption_based_cores(&mut self) {
2983 self.produce_unsat_cores = true;
2984 }
2987
2988 pub fn minimize_unsat_core(&mut self, manager: &mut TermManager) -> Option<UnsatCore> {
2991 if !self.produce_unsat_cores {
2992 return None;
2993 }
2994
2995 let core = self.unsat_core.as_ref()?;
2997 if core.is_empty() {
2998 return Some(core.clone());
2999 }
3000
3001 let mut core_assertions: Vec<_> = core
3003 .indices
3004 .iter()
3005 .map(|&idx| {
3006 let assertion = self.assertions[idx as usize];
3007 let name = self
3008 .named_assertions
3009 .iter()
3010 .find(|na| na.index == idx)
3011 .and_then(|na| na.name.clone());
3012 (idx, assertion, name)
3013 })
3014 .collect();
3015
3016 let mut i = 0;
3018 while i < core_assertions.len() {
3019 let mut temp_solver = Solver::new();
3021 temp_solver.set_logic(self.logic.as_deref().unwrap_or("ALL"));
3022
3023 for (j, &(_, assertion, _)) in core_assertions.iter().enumerate() {
3025 if i != j {
3026 temp_solver.assert(assertion, manager);
3027 }
3028 }
3029
3030 if temp_solver.check(manager) == SolverResult::Unsat {
3032 core_assertions.remove(i);
3034 } else {
3036 i += 1;
3038 }
3039 }
3040
3041 let mut minimized = UnsatCore::new();
3043 for (idx, _, name) in core_assertions {
3044 minimized.indices.push(idx);
3045 if let Some(n) = name {
3046 minimized.names.push(n);
3047 }
3048 }
3049
3050 Some(minimized)
3051 }
3052
3053 #[must_use]
3055 pub fn model(&self) -> Option<&Model> {
3056 self.model.as_ref()
3057 }
3058
3059 pub fn assert_many(&mut self, terms: &[TermId], manager: &mut TermManager) {
3062 for &term in terms {
3063 self.assert(term, manager);
3064 }
3065 }
3066
3067 #[must_use]
3069 pub fn num_assertions(&self) -> usize {
3070 self.assertions.len()
3071 }
3072
3073 #[must_use]
3075 pub fn num_variables(&self) -> usize {
3076 self.term_to_var.len()
3077 }
3078
3079 #[must_use]
3081 pub fn has_assertions(&self) -> bool {
3082 !self.assertions.is_empty()
3083 }
3084
3085 #[must_use]
3087 pub fn context_level(&self) -> usize {
3088 self.context_stack.len()
3089 }
3090
3091 pub fn push(&mut self) {
3093 self.context_stack.push(ContextState {
3094 num_assertions: self.assertions.len(),
3095 num_vars: self.var_to_term.len(),
3096 has_false_assertion: self.has_false_assertion,
3097 trail_position: self.trail.len(),
3098 });
3099 self.sat.push();
3100 self.euf.push();
3101 self.arith.push();
3102 }
3103
3104 pub fn pop(&mut self) {
3106 if let Some(state) = self.context_stack.pop() {
3107 while self.trail.len() > state.trail_position {
3109 if let Some(op) = self.trail.pop() {
3110 match op {
3111 TrailOp::AssertionAdded { index } => {
3112 if self.assertions.len() > index {
3113 self.assertions.truncate(index);
3114 }
3115 }
3116 TrailOp::VarCreated { var: _, term } => {
3117 self.term_to_var.remove(&term);
3119 }
3120 TrailOp::ConstraintAdded { var } => {
3121 self.var_to_constraint.remove(&var);
3123 }
3124 TrailOp::FalseAssertionSet => {
3125 self.has_false_assertion = false;
3127 }
3128 TrailOp::NamedAssertionAdded { index } => {
3129 if self.named_assertions.len() > index {
3131 self.named_assertions.truncate(index);
3132 }
3133 }
3134 TrailOp::BvTermAdded { term } => {
3135 self.bv_terms.remove(&term);
3137 }
3138 TrailOp::ArithTermAdded { term } => {
3139 self.arith_terms.remove(&term);
3141 }
3142 }
3143 }
3144 }
3145
3146 self.assertions.truncate(state.num_assertions);
3148 self.var_to_term.truncate(state.num_vars);
3149 self.has_false_assertion = state.has_false_assertion;
3150
3151 self.sat.pop();
3152 self.euf.pop();
3153 self.arith.pop();
3154 }
3155 }
3156
3157 pub fn reset(&mut self) {
3159 self.sat.reset();
3160 self.euf.reset();
3161 self.arith.reset();
3162 self.bv.reset();
3163 self.term_to_var.clear();
3164 self.var_to_term.clear();
3165 self.var_to_constraint.clear();
3166 self.var_to_parsed_arith.clear();
3167 self.assertions.clear();
3168 self.named_assertions.clear();
3169 self.model = None;
3170 self.unsat_core = None;
3171 self.context_stack.clear();
3172 self.trail.clear();
3173 self.logic = None;
3174 self.theory_processed_up_to = 0;
3175 self.has_false_assertion = false;
3176 self.bv_terms.clear();
3177 self.arith_terms.clear();
3178 }
3179
3180 #[must_use]
3182 pub fn config(&self) -> &SolverConfig {
3183 &self.config
3184 }
3185
3186 pub fn set_config(&mut self, config: SolverConfig) {
3188 self.config = config;
3189 }
3190
3191 #[must_use]
3193 pub fn stats(&self) -> &oxiz_sat::SolverStats {
3194 self.sat.stats()
3195 }
3196}
3197
3198#[cfg(test)]
3199mod tests {
3200 use super::*;
3201
3202 #[test]
3203 fn test_solver_empty() {
3204 let mut solver = Solver::new();
3205 let mut manager = TermManager::new();
3206 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
3207 }
3208
3209 #[test]
3210 fn test_solver_true() {
3211 let mut solver = Solver::new();
3212 let mut manager = TermManager::new();
3213 let t = manager.mk_true();
3214 solver.assert(t, &mut manager);
3215 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
3216 }
3217
3218 #[test]
3219 fn test_solver_false() {
3220 let mut solver = Solver::new();
3221 let mut manager = TermManager::new();
3222 let f = manager.mk_false();
3223 solver.assert(f, &mut manager);
3224 assert_eq!(solver.check(&mut manager), SolverResult::Unsat);
3225 }
3226
3227 #[test]
3228 fn test_solver_push_pop() {
3229 let mut solver = Solver::new();
3230 let mut manager = TermManager::new();
3231
3232 let t = manager.mk_true();
3233 solver.assert(t, &mut manager);
3234 solver.push();
3235
3236 let f = manager.mk_false();
3237 solver.assert(f, &mut manager);
3238 assert_eq!(solver.check(&mut manager), SolverResult::Unsat);
3239
3240 solver.pop();
3241 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
3242 }
3243
3244 #[test]
3245 fn test_unsat_core_trivial() {
3246 let mut solver = Solver::new();
3247 let mut manager = TermManager::new();
3248 solver.set_produce_unsat_cores(true);
3249
3250 let t = manager.mk_true();
3251 let f = manager.mk_false();
3252
3253 solver.assert_named(t, "a1", &mut manager);
3254 solver.assert_named(f, "a2", &mut manager);
3255 solver.assert_named(t, "a3", &mut manager);
3256
3257 assert_eq!(solver.check(&mut manager), SolverResult::Unsat);
3258
3259 let core = solver.get_unsat_core();
3260 assert!(core.is_some());
3261
3262 let core = core.unwrap();
3263 assert!(!core.is_empty());
3264 assert!(core.names.contains(&"a2".to_string()));
3265 }
3266
3267 #[test]
3268 fn test_unsat_core_not_produced_when_sat() {
3269 let mut solver = Solver::new();
3270 let mut manager = TermManager::new();
3271 solver.set_produce_unsat_cores(true);
3272
3273 let t = manager.mk_true();
3274 solver.assert_named(t, "a1", &mut manager);
3275 solver.assert_named(t, "a2", &mut manager);
3276
3277 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
3278 assert!(solver.get_unsat_core().is_none());
3279 }
3280
3281 #[test]
3282 fn test_unsat_core_disabled() {
3283 let mut solver = Solver::new();
3284 let mut manager = TermManager::new();
3285 let f = manager.mk_false();
3288 solver.assert(f, &mut manager);
3289 assert_eq!(solver.check(&mut manager), SolverResult::Unsat);
3290
3291 assert!(solver.get_unsat_core().is_none());
3293 }
3294
3295 #[test]
3296 fn test_boolean_encoding_and() {
3297 let mut solver = Solver::new();
3298 let mut manager = TermManager::new();
3299
3300 let p = manager.mk_var("p", manager.sorts.bool_sort);
3302 let q = manager.mk_var("q", manager.sorts.bool_sort);
3303 let and = manager.mk_and(vec![p, q]);
3304
3305 solver.assert(and, &mut manager);
3306 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
3307
3308 let model = solver.model().expect("Should have model");
3310 assert!(model.get(p).is_some());
3311 assert!(model.get(q).is_some());
3312 }
3313
3314 #[test]
3315 fn test_boolean_encoding_or() {
3316 let mut solver = Solver::new();
3317 let mut manager = TermManager::new();
3318
3319 let p = manager.mk_var("p", manager.sorts.bool_sort);
3321 let q = manager.mk_var("q", manager.sorts.bool_sort);
3322 let or = manager.mk_or(vec![p, q]);
3323 let not_p = manager.mk_not(p);
3324
3325 solver.assert(or, &mut manager);
3326 solver.assert(not_p, &mut manager);
3327 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
3328 }
3329
3330 #[test]
3331 fn test_boolean_encoding_implies() {
3332 let mut solver = Solver::new();
3333 let mut manager = TermManager::new();
3334
3335 let p = manager.mk_var("p", manager.sorts.bool_sort);
3337 let q = manager.mk_var("q", manager.sorts.bool_sort);
3338 let implies = manager.mk_implies(p, q);
3339 let not_q = manager.mk_not(q);
3340
3341 solver.assert(implies, &mut manager);
3342 solver.assert(p, &mut manager);
3343 solver.assert(not_q, &mut manager);
3344 assert_eq!(solver.check(&mut manager), SolverResult::Unsat);
3345 }
3346
3347 #[test]
3348 fn test_boolean_encoding_distinct() {
3349 let mut solver = Solver::new();
3350 let mut manager = TermManager::new();
3351
3352 let p = manager.mk_var("p", manager.sorts.bool_sort);
3354 let q = manager.mk_var("q", manager.sorts.bool_sort);
3355 let r = manager.mk_var("r", manager.sorts.bool_sort);
3356 let distinct = manager.mk_distinct(vec![p, q, r]);
3357
3358 solver.assert(distinct, &mut manager);
3359 solver.assert(p, &mut manager);
3360 solver.assert(q, &mut manager);
3361 assert_eq!(solver.check(&mut manager), SolverResult::Unsat);
3362 }
3363
3364 #[test]
3365 fn test_model_evaluation_bool() {
3366 let mut solver = Solver::new();
3367 let mut manager = TermManager::new();
3368
3369 let p = manager.mk_var("p", manager.sorts.bool_sort);
3370 let q = manager.mk_var("q", manager.sorts.bool_sort);
3371
3372 solver.assert(p, &mut manager);
3374 solver.assert(manager.mk_not(q), &mut manager);
3375 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
3376
3377 let model = solver.model().expect("Should have model");
3378
3379 let p_val = model.eval(p, &mut manager);
3381 assert_eq!(p_val, manager.mk_true());
3382
3383 let q_val = model.eval(q, &mut manager);
3385 assert_eq!(q_val, manager.mk_false());
3386
3387 let and_term = manager.mk_and(vec![p, q]);
3389 let and_val = model.eval(and_term, &mut manager);
3390 assert_eq!(and_val, manager.mk_false());
3391
3392 let or_term = manager.mk_or(vec![p, q]);
3394 let or_val = model.eval(or_term, &mut manager);
3395 assert_eq!(or_val, manager.mk_true());
3396 }
3397
3398 #[test]
3399 fn test_model_evaluation_ite() {
3400 let mut solver = Solver::new();
3401 let mut manager = TermManager::new();
3402
3403 let p = manager.mk_var("p", manager.sorts.bool_sort);
3404 let q = manager.mk_var("q", manager.sorts.bool_sort);
3405 let r = manager.mk_var("r", manager.sorts.bool_sort);
3406
3407 solver.assert(p, &mut manager);
3409 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
3410
3411 let model = solver.model().expect("Should have model");
3412
3413 let ite_term = manager.mk_ite(p, q, r);
3415 let ite_val = model.eval(ite_term, &mut manager);
3416 let q_val = model.eval(q, &mut manager);
3418 assert_eq!(ite_val, q_val);
3419 }
3420
3421 #[test]
3422 fn test_model_evaluation_implies() {
3423 let mut solver = Solver::new();
3424 let mut manager = TermManager::new();
3425
3426 let p = manager.mk_var("p", manager.sorts.bool_sort);
3427 let q = manager.mk_var("q", manager.sorts.bool_sort);
3428
3429 solver.assert(manager.mk_not(p), &mut manager);
3431 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
3432
3433 let model = solver.model().expect("Should have model");
3434
3435 let implies_term = manager.mk_implies(p, q);
3437 let implies_val = model.eval(implies_term, &mut manager);
3438 assert_eq!(implies_val, manager.mk_true());
3439 }
3440
3441 #[test]
3442 fn test_bv_comparison_model_generation() {
3443 let mut solver = Solver::new();
3445 let mut manager = TermManager::new();
3446
3447 solver.set_logic("QF_BV");
3448
3449 let bv8_sort = manager.sorts.bitvec(8);
3451 let x = manager.mk_var("x", bv8_sort);
3452
3453 let five = manager.mk_bitvec(5i64, 8);
3455 let ten = manager.mk_bitvec(10i64, 8);
3456
3457 let lt1 = manager.mk_bv_ult(five, x);
3459 solver.assert(lt1, &mut manager);
3460
3461 let lt2 = manager.mk_bv_ult(x, ten);
3463 solver.assert(lt2, &mut manager);
3464
3465 let result = solver.check(&mut manager);
3466 assert_eq!(result, SolverResult::Sat);
3467
3468 let model = solver.model().expect("Should have model");
3470
3471 if let Some(x_value_id) = model.get(x) {
3473 if let Some(x_term) = manager.get(x_value_id) {
3474 if let TermKind::BitVecConst { value, .. } = &x_term.kind {
3475 let x_val = value.to_u64().unwrap_or(0);
3476 assert!(
3478 x_val >= 6 && x_val <= 9,
3479 "Expected x in [6,9], got {}",
3480 x_val
3481 );
3482 }
3483 }
3484 }
3485 }
3486
3487 #[test]
3488 fn test_arithmetic_model_generation() {
3489 use num_bigint::BigInt;
3490
3491 let mut solver = Solver::new();
3492 let mut manager = TermManager::new();
3493
3494 let x = manager.mk_var("x", manager.sorts.int_sort);
3496 let y = manager.mk_var("y", manager.sorts.int_sort);
3497
3498 let ten = manager.mk_int(BigInt::from(10));
3500 let zero = manager.mk_int(BigInt::from(0));
3501 let sum = manager.mk_add(vec![x, y]);
3502
3503 let eq = manager.mk_eq(sum, ten);
3504 let x_ge_0 = manager.mk_ge(x, zero);
3505 let y_ge_0 = manager.mk_ge(y, zero);
3506
3507 solver.assert(eq, &mut manager);
3508 solver.assert(x_ge_0, &mut manager);
3509 solver.assert(y_ge_0, &mut manager);
3510
3511 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
3512
3513 let model = solver.model();
3515 assert!(model.is_some(), "Should have a model for SAT result");
3516 }
3517
3518 #[test]
3519 fn test_model_pretty_print() {
3520 let mut solver = Solver::new();
3521 let mut manager = TermManager::new();
3522
3523 let p = manager.mk_var("p", manager.sorts.bool_sort);
3524 let q = manager.mk_var("q", manager.sorts.bool_sort);
3525
3526 solver.assert(p, &mut manager);
3527 solver.assert(manager.mk_not(q), &mut manager);
3528 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
3529
3530 let model = solver.model().expect("Should have model");
3531 let pretty = model.pretty_print(&manager);
3532
3533 assert!(pretty.contains("(model"));
3535 assert!(pretty.contains("define-fun"));
3536 assert!(pretty.contains("p") || pretty.contains("q"));
3538 }
3539
3540 #[test]
3541 fn test_trail_based_undo_assertions() {
3542 let mut solver = Solver::new();
3543 let mut manager = TermManager::new();
3544
3545 let p = manager.mk_var("p", manager.sorts.bool_sort);
3546 let q = manager.mk_var("q", manager.sorts.bool_sort);
3547
3548 assert_eq!(solver.assertions.len(), 0);
3550 assert_eq!(solver.trail.len(), 0);
3551
3552 solver.assert(p, &mut manager);
3554 assert_eq!(solver.assertions.len(), 1);
3555 assert!(!solver.trail.is_empty());
3556
3557 solver.push();
3559 let trail_len_after_push = solver.trail.len();
3560 solver.assert(q, &mut manager);
3561 assert_eq!(solver.assertions.len(), 2);
3562 assert!(solver.trail.len() > trail_len_after_push);
3563
3564 solver.pop();
3566 assert_eq!(solver.assertions.len(), 1);
3567 assert_eq!(solver.assertions[0], p);
3568 }
3569
3570 #[test]
3571 fn test_trail_based_undo_variables() {
3572 let mut solver = Solver::new();
3573 let mut manager = TermManager::new();
3574
3575 let p = manager.mk_var("p", manager.sorts.bool_sort);
3576 let q = manager.mk_var("q", manager.sorts.bool_sort);
3577
3578 solver.assert(p, &mut manager);
3580 let initial_var_count = solver.term_to_var.len();
3581
3582 solver.push();
3584 solver.assert(q, &mut manager);
3585 assert!(solver.term_to_var.len() >= initial_var_count);
3586
3587 solver.pop();
3589 assert_eq!(solver.assertions.len(), 1);
3591 }
3592
3593 #[test]
3594 fn test_trail_based_undo_constraints() {
3595 use num_bigint::BigInt;
3596
3597 let mut solver = Solver::new();
3598 let mut manager = TermManager::new();
3599
3600 let x = manager.mk_var("x", manager.sorts.int_sort);
3601 let zero = manager.mk_int(BigInt::from(0));
3602
3603 let c1 = manager.mk_ge(x, zero);
3605 solver.assert(c1, &mut manager);
3606 let initial_constraint_count = solver.var_to_constraint.len();
3607
3608 solver.push();
3610 let ten = manager.mk_int(BigInt::from(10));
3611 let c2 = manager.mk_le(x, ten);
3612 solver.assert(c2, &mut manager);
3613 assert!(solver.var_to_constraint.len() >= initial_constraint_count);
3614
3615 solver.pop();
3617 assert_eq!(solver.var_to_constraint.len(), initial_constraint_count);
3618 }
3619
3620 #[test]
3621 fn test_trail_based_undo_false_assertion() {
3622 let mut solver = Solver::new();
3623 let mut manager = TermManager::new();
3624
3625 assert!(!solver.has_false_assertion);
3626
3627 solver.push();
3628 solver.assert(manager.mk_false(), &mut manager);
3629 assert!(solver.has_false_assertion);
3630
3631 solver.pop();
3632 assert!(!solver.has_false_assertion);
3633 }
3634
3635 #[test]
3636 fn test_trail_based_undo_named_assertions() {
3637 let mut solver = Solver::new();
3638 let mut manager = TermManager::new();
3639 solver.set_produce_unsat_cores(true);
3640
3641 let p = manager.mk_var("p", manager.sorts.bool_sort);
3642
3643 solver.assert_named(p, "assertion1", &mut manager);
3644 assert_eq!(solver.named_assertions.len(), 1);
3645
3646 solver.push();
3647 let q = manager.mk_var("q", manager.sorts.bool_sort);
3648 solver.assert_named(q, "assertion2", &mut manager);
3649 assert_eq!(solver.named_assertions.len(), 2);
3650
3651 solver.pop();
3652 assert_eq!(solver.named_assertions.len(), 1);
3653 assert_eq!(
3654 solver.named_assertions[0].name,
3655 Some("assertion1".to_string())
3656 );
3657 }
3658
3659 #[test]
3660 fn test_trail_based_undo_nested_push_pop() {
3661 let mut solver = Solver::new();
3662 let mut manager = TermManager::new();
3663
3664 let p = manager.mk_var("p", manager.sorts.bool_sort);
3665 solver.assert(p, &mut manager);
3666
3667 solver.push();
3669 let q = manager.mk_var("q", manager.sorts.bool_sort);
3670 solver.assert(q, &mut manager);
3671 assert_eq!(solver.assertions.len(), 2);
3672
3673 solver.push();
3675 let r = manager.mk_var("r", manager.sorts.bool_sort);
3676 solver.assert(r, &mut manager);
3677 assert_eq!(solver.assertions.len(), 3);
3678
3679 solver.pop();
3681 assert_eq!(solver.assertions.len(), 2);
3682
3683 solver.pop();
3685 assert_eq!(solver.assertions.len(), 1);
3686 assert_eq!(solver.assertions[0], p);
3687 }
3688
3689 #[test]
3690 fn test_config_presets() {
3691 let _fast = SolverConfig::fast();
3693 let _balanced = SolverConfig::balanced();
3694 let _thorough = SolverConfig::thorough();
3695 let _minimal = SolverConfig::minimal();
3696 }
3697
3698 #[test]
3699 fn test_config_fast_characteristics() {
3700 let config = SolverConfig::fast();
3701
3702 assert!(!config.enable_variable_elimination);
3704 assert!(!config.enable_blocked_clause_elimination);
3705 assert!(!config.enable_symmetry_breaking);
3706 assert!(!config.enable_inprocessing);
3707 assert!(!config.enable_clause_subsumption);
3708
3709 assert!(config.enable_clause_minimization);
3711 assert!(config.simplify);
3712
3713 assert_eq!(config.restart_strategy, RestartStrategy::Geometric);
3715 }
3716
3717 #[test]
3718 fn test_config_balanced_characteristics() {
3719 let config = SolverConfig::balanced();
3720
3721 assert!(config.enable_variable_elimination);
3723 assert!(config.enable_blocked_clause_elimination);
3724 assert!(config.enable_inprocessing);
3725 assert!(config.enable_clause_minimization);
3726 assert!(config.enable_clause_subsumption);
3727 assert!(config.simplify);
3728
3729 assert!(!config.enable_symmetry_breaking);
3731
3732 assert_eq!(config.restart_strategy, RestartStrategy::Glucose);
3734
3735 assert_eq!(config.variable_elimination_limit, 1000);
3737 assert_eq!(config.inprocessing_interval, 10000);
3738 }
3739
3740 #[test]
3741 fn test_config_thorough_characteristics() {
3742 let config = SolverConfig::thorough();
3743
3744 assert!(config.enable_variable_elimination);
3746 assert!(config.enable_blocked_clause_elimination);
3747 assert!(config.enable_symmetry_breaking); assert!(config.enable_inprocessing);
3749 assert!(config.enable_clause_minimization);
3750 assert!(config.enable_clause_subsumption);
3751 assert!(config.simplify);
3752
3753 assert_eq!(config.variable_elimination_limit, 5000);
3755 assert_eq!(config.inprocessing_interval, 5000);
3756 }
3757
3758 #[test]
3759 fn test_config_minimal_characteristics() {
3760 let config = SolverConfig::minimal();
3761
3762 assert!(!config.simplify);
3764 assert!(!config.enable_variable_elimination);
3765 assert!(!config.enable_blocked_clause_elimination);
3766 assert!(!config.enable_symmetry_breaking);
3767 assert!(!config.enable_inprocessing);
3768 assert!(!config.enable_clause_minimization);
3769 assert!(!config.enable_clause_subsumption);
3770
3771 assert_eq!(config.theory_mode, TheoryMode::Lazy);
3773
3774 assert_eq!(config.num_threads, 1);
3776 }
3777
3778 #[test]
3779 fn test_config_builder_pattern() {
3780 let config = SolverConfig::fast()
3782 .with_proof()
3783 .with_timeout(5000)
3784 .with_max_conflicts(1000)
3785 .with_max_decisions(2000)
3786 .with_parallel(8)
3787 .with_restart_strategy(RestartStrategy::Luby)
3788 .with_theory_mode(TheoryMode::Lazy);
3789
3790 assert!(config.proof);
3791 assert_eq!(config.timeout_ms, 5000);
3792 assert_eq!(config.max_conflicts, 1000);
3793 assert_eq!(config.max_decisions, 2000);
3794 assert!(config.parallel);
3795 assert_eq!(config.num_threads, 8);
3796 assert_eq!(config.restart_strategy, RestartStrategy::Luby);
3797 assert_eq!(config.theory_mode, TheoryMode::Lazy);
3798 }
3799
3800 #[test]
3801 fn test_solver_with_different_configs() {
3802 let mut manager = TermManager::new();
3803
3804 let mut solver_fast = Solver::with_config(SolverConfig::fast());
3806 let mut solver_balanced = Solver::with_config(SolverConfig::balanced());
3807 let mut solver_thorough = Solver::with_config(SolverConfig::thorough());
3808 let mut solver_minimal = Solver::with_config(SolverConfig::minimal());
3809
3810 let t = manager.mk_true();
3812 solver_fast.assert(t, &mut manager);
3813 solver_balanced.assert(t, &mut manager);
3814 solver_thorough.assert(t, &mut manager);
3815 solver_minimal.assert(t, &mut manager);
3816
3817 assert_eq!(solver_fast.check(&mut manager), SolverResult::Sat);
3818 assert_eq!(solver_balanced.check(&mut manager), SolverResult::Sat);
3819 assert_eq!(solver_thorough.check(&mut manager), SolverResult::Sat);
3820 assert_eq!(solver_minimal.check(&mut manager), SolverResult::Sat);
3821 }
3822
3823 #[test]
3824 fn test_config_default_is_balanced() {
3825 let default = SolverConfig::default();
3826 let balanced = SolverConfig::balanced();
3827
3828 assert_eq!(
3830 default.enable_variable_elimination,
3831 balanced.enable_variable_elimination
3832 );
3833 assert_eq!(
3834 default.enable_clause_minimization,
3835 balanced.enable_clause_minimization
3836 );
3837 assert_eq!(
3838 default.enable_symmetry_breaking,
3839 balanced.enable_symmetry_breaking
3840 );
3841 assert_eq!(default.restart_strategy, balanced.restart_strategy);
3842 }
3843
3844 #[test]
3845 fn test_theory_combination_arith_solver() {
3846 use oxiz_theories::arithmetic::ArithSolver;
3847 use oxiz_theories::{EqualityNotification, TheoryCombination};
3848
3849 let mut arith = ArithSolver::lra();
3850 let mut manager = TermManager::new();
3851
3852 let x = manager.mk_var("x", manager.sorts.int_sort);
3854 let y = manager.mk_var("y", manager.sorts.int_sort);
3855
3856 let _x_var = arith.intern(x);
3858 let _y_var = arith.intern(y);
3859
3860 let eq_notification = EqualityNotification {
3862 lhs: x,
3863 rhs: y,
3864 reason: None,
3865 };
3866
3867 let accepted = arith.notify_equality(eq_notification);
3868 assert!(
3869 accepted,
3870 "ArithSolver should accept equality notification for known terms"
3871 );
3872
3873 assert!(
3875 arith.is_relevant(x),
3876 "x should be relevant to arithmetic solver"
3877 );
3878 assert!(
3879 arith.is_relevant(y),
3880 "y should be relevant to arithmetic solver"
3881 );
3882
3883 let z = manager.mk_var("z", manager.sorts.int_sort);
3885 assert!(
3886 !arith.is_relevant(z),
3887 "z should not be relevant (not interned)"
3888 );
3889
3890 let eq_unknown = EqualityNotification {
3892 lhs: x,
3893 rhs: z,
3894 reason: None,
3895 };
3896 let accepted_unknown = arith.notify_equality(eq_unknown);
3897 assert!(
3898 !accepted_unknown,
3899 "ArithSolver should reject equality with unknown term"
3900 );
3901 }
3902
3903 #[test]
3904 fn test_theory_combination_get_shared_equalities() {
3905 use oxiz_theories::TheoryCombination;
3906 use oxiz_theories::arithmetic::ArithSolver;
3907
3908 let arith = ArithSolver::lra();
3909
3910 let shared = arith.get_shared_equalities();
3912 assert!(
3913 shared.is_empty(),
3914 "ArithSolver should return empty shared equalities (placeholder)"
3915 );
3916 }
3917
3918 #[test]
3919 fn test_equality_notification_fields() {
3920 use oxiz_theories::EqualityNotification;
3921
3922 let mut manager = TermManager::new();
3923 let x = manager.mk_var("x", manager.sorts.int_sort);
3924 let y = manager.mk_var("y", manager.sorts.int_sort);
3925
3926 let eq1 = EqualityNotification {
3928 lhs: x,
3929 rhs: y,
3930 reason: Some(x),
3931 };
3932 assert_eq!(eq1.lhs, x);
3933 assert_eq!(eq1.rhs, y);
3934 assert_eq!(eq1.reason, Some(x));
3935
3936 let eq2 = EqualityNotification {
3938 lhs: x,
3939 rhs: y,
3940 reason: None,
3941 };
3942 assert_eq!(eq2.reason, None);
3943
3944 let eq3 = eq1;
3946 assert_eq!(eq3.lhs, eq1.lhs);
3947 assert_eq!(eq3.rhs, eq1.rhs);
3948 }
3949
3950 #[test]
3951 fn test_assert_many() {
3952 let mut solver = Solver::new();
3953 let mut manager = TermManager::new();
3954
3955 let p = manager.mk_var("p", manager.sorts.bool_sort);
3956 let q = manager.mk_var("q", manager.sorts.bool_sort);
3957 let r = manager.mk_var("r", manager.sorts.bool_sort);
3958
3959 solver.assert_many(&[p, q, r], &mut manager);
3961
3962 assert_eq!(solver.num_assertions(), 3);
3963 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
3964 }
3965
3966 #[test]
3967 fn test_num_assertions_and_variables() {
3968 let mut solver = Solver::new();
3969 let mut manager = TermManager::new();
3970
3971 assert_eq!(solver.num_assertions(), 0);
3972 assert!(!solver.has_assertions());
3973
3974 let p = manager.mk_var("p", manager.sorts.bool_sort);
3975 let q = manager.mk_var("q", manager.sorts.bool_sort);
3976
3977 solver.assert(p, &mut manager);
3978 assert_eq!(solver.num_assertions(), 1);
3979 assert!(solver.has_assertions());
3980
3981 solver.assert(q, &mut manager);
3982 assert_eq!(solver.num_assertions(), 2);
3983
3984 assert!(solver.num_variables() > 0);
3986 }
3987
3988 #[test]
3989 fn test_context_level() {
3990 let mut solver = Solver::new();
3991
3992 assert_eq!(solver.context_level(), 0);
3993
3994 solver.push();
3995 assert_eq!(solver.context_level(), 1);
3996
3997 solver.push();
3998 assert_eq!(solver.context_level(), 2);
3999
4000 solver.pop();
4001 assert_eq!(solver.context_level(), 1);
4002
4003 solver.pop();
4004 assert_eq!(solver.context_level(), 0);
4005 }
4006
4007 #[test]
4010 fn test_quantifier_basic_forall() {
4011 let mut solver = Solver::new();
4012 let mut manager = TermManager::new();
4013 let bool_sort = manager.sorts.bool_sort;
4014
4015 let x = manager.mk_var("x", bool_sort);
4018 let p_x = manager.mk_apply("P", [x], bool_sort);
4019 let forall = manager.mk_forall([("x", bool_sort)], p_x);
4020
4021 solver.assert(forall, &mut manager);
4022
4023 let result = solver.check(&mut manager);
4025 assert!(
4027 result == SolverResult::Sat || result == SolverResult::Unknown,
4028 "Basic forall should not be unsat"
4029 );
4030 }
4031
4032 #[test]
4033 fn test_quantifier_basic_exists() {
4034 let mut solver = Solver::new();
4035 let mut manager = TermManager::new();
4036 let bool_sort = manager.sorts.bool_sort;
4037
4038 let x = manager.mk_var("x", bool_sort);
4040 let p_x = manager.mk_apply("P", [x], bool_sort);
4041 let exists = manager.mk_exists([("x", bool_sort)], p_x);
4042
4043 solver.assert(exists, &mut manager);
4044
4045 let result = solver.check(&mut manager);
4046 assert!(
4047 result == SolverResult::Sat || result == SolverResult::Unknown,
4048 "Basic exists should not be unsat"
4049 );
4050 }
4051
4052 #[test]
4053 fn test_quantifier_with_ground_terms() {
4054 let mut solver = Solver::new();
4055 let mut manager = TermManager::new();
4056 let int_sort = manager.sorts.int_sort;
4057 let bool_sort = manager.sorts.bool_sort;
4058
4059 let zero = manager.mk_int(0);
4061 let one = manager.mk_int(1);
4062
4063 let p_0 = manager.mk_apply("P", [zero], bool_sort);
4065 let p_1 = manager.mk_apply("P", [one], bool_sort);
4066 solver.assert(p_0, &mut manager);
4067 solver.assert(p_1, &mut manager);
4068
4069 let x = manager.mk_var("x", int_sort);
4071 let p_x = manager.mk_apply("P", [x], bool_sort);
4072 let forall = manager.mk_forall([("x", int_sort)], p_x);
4073 solver.assert(forall, &mut manager);
4074
4075 let result = solver.check(&mut manager);
4076 assert!(
4078 result == SolverResult::Sat || result == SolverResult::Unknown,
4079 "Quantifier with matching ground terms should be satisfiable"
4080 );
4081 }
4082
4083 #[test]
4084 fn test_quantifier_instantiation() {
4085 let mut solver = Solver::new();
4086 let mut manager = TermManager::new();
4087 let int_sort = manager.sorts.int_sort;
4088 let bool_sort = manager.sorts.bool_sort;
4089
4090 let c = manager.mk_apply("c", [], int_sort);
4092
4093 let x = manager.mk_var("x", int_sort);
4095 let f_x = manager.mk_apply("f", [x], int_sort);
4096 let zero = manager.mk_int(0);
4097 let f_x_gt_0 = manager.mk_gt(f_x, zero);
4098 let forall = manager.mk_forall([("x", int_sort)], f_x_gt_0);
4099 solver.assert(forall, &mut manager);
4100
4101 let f_c = manager.mk_apply("f", [c], int_sort);
4103 let f_c_exists = manager.mk_apply("exists_f_c", [f_c], bool_sort);
4104 solver.assert(f_c_exists, &mut manager);
4105
4106 let result = solver.check(&mut manager);
4107 assert!(
4109 result == SolverResult::Sat || result == SolverResult::Unknown,
4110 "Quantifier instantiation test"
4111 );
4112 }
4113
4114 #[test]
4115 fn test_quantifier_mbqi_solver_integration() {
4116 use crate::mbqi::MBQISolver;
4117
4118 let mut mbqi = MBQISolver::new();
4119 let mut manager = TermManager::new();
4120 let int_sort = manager.sorts.int_sort;
4121
4122 let x = manager.mk_var("x", int_sort);
4124 let zero = manager.mk_int(0);
4125 let x_gt_0 = manager.mk_gt(x, zero);
4126 let forall = manager.mk_forall([("x", int_sort)], x_gt_0);
4127
4128 mbqi.add_quantifier(forall, &manager);
4130
4131 let one = manager.mk_int(1);
4133 let two = manager.mk_int(2);
4134 mbqi.add_candidate(one, int_sort);
4135 mbqi.add_candidate(two, int_sort);
4136
4137 assert!(mbqi.is_enabled(), "MBQI should be enabled by default");
4139 }
4140
4141 #[test]
4142 fn test_quantifier_pattern_matching() {
4143 let mut solver = Solver::new();
4144 let mut manager = TermManager::new();
4145 let int_sort = manager.sorts.int_sort;
4146
4147 let x = manager.mk_var("x", int_sort);
4149 let f_x = manager.mk_apply("f", [x], int_sort);
4150 let g_x = manager.mk_apply("g", [x], int_sort);
4151 let body = manager.mk_eq(f_x, g_x);
4152
4153 let pattern: smallvec::SmallVec<[_; 2]> = smallvec::smallvec![f_x];
4155 let patterns: smallvec::SmallVec<[_; 2]> = smallvec::smallvec![pattern];
4156
4157 let forall = manager.mk_forall_with_patterns([("x", int_sort)], body, patterns);
4158 solver.assert(forall, &mut manager);
4159
4160 let c = manager.mk_apply("c", [], int_sort);
4162 let f_c = manager.mk_apply("f", [c], int_sort);
4163 let f_c_eq_c = manager.mk_eq(f_c, c);
4164 solver.assert(f_c_eq_c, &mut manager);
4165
4166 let result = solver.check(&mut manager);
4167 assert!(
4168 result == SolverResult::Sat || result == SolverResult::Unknown,
4169 "Pattern matching should allow instantiation"
4170 );
4171 }
4172
4173 #[test]
4174 fn test_quantifier_multiple() {
4175 let mut solver = Solver::new();
4176 let mut manager = TermManager::new();
4177 let int_sort = manager.sorts.int_sort;
4178
4179 let x = manager.mk_var("x", int_sort);
4181 let y = manager.mk_var("y", int_sort);
4182 let x_plus_y = manager.mk_add([x, y]);
4183 let y_plus_x = manager.mk_add([y, x]);
4184 let commutative = manager.mk_eq(x_plus_y, y_plus_x);
4185
4186 let inner_forall = manager.mk_forall([("y", int_sort)], commutative);
4187 let outer_forall = manager.mk_forall([("x", int_sort)], inner_forall);
4188
4189 solver.assert(outer_forall, &mut manager);
4190
4191 let result = solver.check(&mut manager);
4192 assert!(
4193 result == SolverResult::Sat || result == SolverResult::Unknown,
4194 "Nested forall should be handled"
4195 );
4196 }
4197
4198 #[test]
4199 fn test_quantifier_with_model() {
4200 let mut solver = Solver::new();
4201 let mut manager = TermManager::new();
4202 let bool_sort = manager.sorts.bool_sort;
4203
4204 let p = manager.mk_var("p", bool_sort);
4206 solver.assert(p, &mut manager);
4207
4208 let x = manager.mk_var("x", bool_sort);
4210 let not_x = manager.mk_not(x);
4211 let x_or_not_x = manager.mk_or([x, not_x]);
4212 let tautology = manager.mk_forall([("x", bool_sort)], x_or_not_x);
4213 solver.assert(tautology, &mut manager);
4214
4215 let result = solver.check(&mut manager);
4216 assert_eq!(
4217 result,
4218 SolverResult::Sat,
4219 "Tautology in quantifier should be SAT"
4220 );
4221
4222 if let Some(model) = solver.model() {
4224 assert!(model.size() > 0, "Model should have assignments");
4225 }
4226 }
4227
4228 #[test]
4229 fn test_quantifier_push_pop() {
4230 let mut solver = Solver::new();
4231 let mut manager = TermManager::new();
4232 let int_sort = manager.sorts.int_sort;
4233
4234 let x = manager.mk_var("x", int_sort);
4236 let zero = manager.mk_int(0);
4237 let x_gt_0 = manager.mk_gt(x, zero);
4238 let forall = manager.mk_forall([("x", int_sort)], x_gt_0);
4239
4240 solver.push();
4241 solver.assert(forall, &mut manager);
4242
4243 let result1 = solver.check(&mut manager);
4244 assert!(
4247 result1 == SolverResult::Unsat || result1 == SolverResult::Unknown,
4248 "forall x. x > 0 should be Unsat or Unknown, got {:?}",
4249 result1
4250 );
4251
4252 solver.pop();
4253
4254 let result2 = solver.check(&mut manager);
4256 assert_eq!(
4257 result2,
4258 SolverResult::Sat,
4259 "After pop, should be trivially SAT"
4260 );
4261 }
4262
4263 #[test]
4270 #[ignore = "Requires complete arithmetic constraint encoding to simplex (see process_constraint in solver.rs)"]
4271 fn test_integer_contradiction_unsat() {
4272 use num_bigint::BigInt;
4273
4274 let mut solver = Solver::new();
4275 let mut manager = TermManager::new();
4276
4277 let x = manager.mk_var("x", manager.sorts.int_sort);
4279 let zero = manager.mk_int(BigInt::from(0));
4280
4281 let x_ge_0 = manager.mk_ge(x, zero);
4283 solver.assert(x_ge_0, &mut manager);
4284
4285 let x_lt_0 = manager.mk_lt(x, zero);
4287 solver.assert(x_lt_0, &mut manager);
4288
4289 let result = solver.check(&mut manager);
4291 assert_eq!(
4292 result,
4293 SolverResult::Unsat,
4294 "x >= 0 AND x < 0 should be UNSAT"
4295 );
4296 }
4297}