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;
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}
852
853#[derive(Debug, Clone, Copy)]
855#[allow(dead_code)]
856pub struct TheoryDecision {
857 pub var: Var,
859 pub value: bool,
861 pub priority: i32,
863}
864
865struct TheoryManager<'a> {
867 euf: &'a mut EufSolver,
869 arith: &'a mut ArithSolver,
871 bv: &'a mut BvSolver,
873 var_to_constraint: &'a FxHashMap<Var, Constraint>,
875 var_to_parsed_arith: &'a FxHashMap<Var, ParsedArithConstraint>,
877 term_to_var: &'a FxHashMap<TermId, Var>,
879 level_stack: Vec<usize>,
881 processed_count: usize,
883 theory_mode: TheoryMode,
885 pending_assignments: Vec<(Lit, bool)>,
887 #[allow(dead_code)]
889 decision_hints: Vec<TheoryDecision>,
890 pending_equalities: Vec<EqualityNotification>,
892 processed_equalities: FxHashMap<(TermId, TermId), bool>,
894 statistics: &'a mut Statistics,
896 max_conflicts: u64,
898 #[allow(dead_code)]
900 max_decisions: u64,
901}
902
903impl<'a> TheoryManager<'a> {
904 #[allow(clippy::too_many_arguments)]
905 fn new(
906 euf: &'a mut EufSolver,
907 arith: &'a mut ArithSolver,
908 bv: &'a mut BvSolver,
909 var_to_constraint: &'a FxHashMap<Var, Constraint>,
910 var_to_parsed_arith: &'a FxHashMap<Var, ParsedArithConstraint>,
911 term_to_var: &'a FxHashMap<TermId, Var>,
912 theory_mode: TheoryMode,
913 statistics: &'a mut Statistics,
914 max_conflicts: u64,
915 max_decisions: u64,
916 ) -> Self {
917 Self {
918 euf,
919 arith,
920 bv,
921 var_to_constraint,
922 var_to_parsed_arith,
923 term_to_var,
924 level_stack: vec![0],
925 processed_count: 0,
926 theory_mode,
927 pending_assignments: Vec::new(),
928 decision_hints: Vec::new(),
929 pending_equalities: Vec::new(),
930 processed_equalities: FxHashMap::default(),
931 statistics,
932 max_conflicts,
933 max_decisions,
934 }
935 }
936
937 #[allow(dead_code)]
940 fn propagate_equalities(&mut self) -> TheoryCheckResult {
941 while let Some(eq) = self.pending_equalities.pop() {
943 let key = if eq.lhs < eq.rhs {
945 (eq.lhs, eq.rhs)
946 } else {
947 (eq.rhs, eq.lhs)
948 };
949
950 if self.processed_equalities.contains_key(&key) {
951 continue;
952 }
953 self.processed_equalities.insert(key, true);
954
955 let lhs_node = self.euf.intern(eq.lhs);
957 let rhs_node = self.euf.intern(eq.rhs);
958 if let Err(_e) = self
959 .euf
960 .merge(lhs_node, rhs_node, eq.reason.unwrap_or(eq.lhs))
961 {
962 continue;
964 }
965
966 if let Some(conflict_terms) = self.euf.check_conflicts() {
968 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
969 return TheoryCheckResult::Conflict(conflict_lits);
970 }
971
972 self.arith.notify_equality(eq);
974 }
975
976 TheoryCheckResult::Sat
977 }
978
979 #[allow(dead_code)]
983 fn model_based_combination(&mut self) -> TheoryCheckResult {
984 let mut shared_terms: Vec<TermId> = Vec::new();
986
987 for &term in self.term_to_var.keys() {
990 shared_terms.push(term);
991 }
992
993 if shared_terms.len() < 2 {
994 return TheoryCheckResult::Sat;
995 }
996
997 for i in 0..shared_terms.len() {
1000 for j in (i + 1)..shared_terms.len() {
1001 let t1 = shared_terms[i];
1002 let t2 = shared_terms[j];
1003
1004 let t1_node = self.euf.intern(t1);
1006 let t2_node = self.euf.intern(t2);
1007
1008 if self.euf.are_equal(t1_node, t2_node) {
1009 let t1_value = self.arith.value(t1);
1012 let t2_value = self.arith.value(t2);
1013
1014 if let (Some(v1), Some(v2)) = (t1_value, t2_value)
1015 && v1 != v2
1016 {
1017 let conflict_lits = self.terms_to_conflict_clause(&[t1, t2]);
1021 return TheoryCheckResult::Conflict(conflict_lits);
1022 }
1023 }
1024 }
1025 }
1026
1027 TheoryCheckResult::Sat
1028 }
1029
1030 #[allow(dead_code)]
1032 fn add_shared_equality(&mut self, lhs: TermId, rhs: TermId, reason: Option<TermId>) {
1033 self.pending_equalities
1034 .push(EqualityNotification { lhs, rhs, reason });
1035 }
1036
1037 #[allow(dead_code)]
1040 fn get_decision_hints(&mut self) -> &[TheoryDecision] {
1041 self.decision_hints.clear();
1043
1044 &self.decision_hints
1055 }
1056
1057 fn terms_to_conflict_clause(&self, terms: &[TermId]) -> SmallVec<[Lit; 8]> {
1060 let mut conflict = SmallVec::new();
1061 for &term in terms {
1062 if let Some(&var) = self.term_to_var.get(&term) {
1063 conflict.push(Lit::neg(var));
1065 }
1066 }
1067 conflict
1068 }
1069
1070 fn process_constraint(
1072 &mut self,
1073 var: Var,
1074 constraint: Constraint,
1075 is_positive: bool,
1076 ) -> TheoryCheckResult {
1077 match constraint {
1078 Constraint::Eq(lhs, rhs) => {
1079 if is_positive {
1080 let lhs_node = self.euf.intern(lhs);
1082 let rhs_node = self.euf.intern(rhs);
1083 if let Err(_e) = self.euf.merge(lhs_node, rhs_node, lhs) {
1084 return TheoryCheckResult::Sat;
1086 }
1087
1088 if let Some(conflict_terms) = self.euf.check_conflicts() {
1090 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1092 return TheoryCheckResult::Conflict(conflict_lits);
1093 }
1094 } else {
1095 let lhs_node = self.euf.intern(lhs);
1097 let rhs_node = self.euf.intern(rhs);
1098 self.euf.assert_diseq(lhs_node, rhs_node, lhs);
1099
1100 if let Some(conflict_terms) = self.euf.check_conflicts() {
1102 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1103 return TheoryCheckResult::Conflict(conflict_lits);
1104 }
1105 }
1106 }
1107 Constraint::Diseq(lhs, rhs) => {
1108 if is_positive {
1109 let lhs_node = self.euf.intern(lhs);
1111 let rhs_node = self.euf.intern(rhs);
1112 self.euf.assert_diseq(lhs_node, rhs_node, lhs);
1113
1114 if let Some(conflict_terms) = self.euf.check_conflicts() {
1115 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1116 return TheoryCheckResult::Conflict(conflict_lits);
1117 }
1118 } else {
1119 let lhs_node = self.euf.intern(lhs);
1121 let rhs_node = self.euf.intern(rhs);
1122 if let Err(_e) = self.euf.merge(lhs_node, rhs_node, lhs) {
1123 return TheoryCheckResult::Sat;
1124 }
1125
1126 if let Some(conflict_terms) = self.euf.check_conflicts() {
1127 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1128 return TheoryCheckResult::Conflict(conflict_lits);
1129 }
1130 }
1131 }
1132 Constraint::Lt(_lhs, _rhs)
1134 | Constraint::Le(_lhs, _rhs)
1135 | Constraint::Gt(_lhs, _rhs)
1136 | Constraint::Ge(_lhs, _rhs) => {
1137 if let Some(parsed) = self.var_to_parsed_arith.get(&var) {
1139 let terms: Vec<(TermId, Rational64)> = parsed.terms.iter().copied().collect();
1141 let reason = parsed.reason_term;
1142 let constant = parsed.constant;
1143
1144 if is_positive {
1145 match parsed.constraint_type {
1147 ArithConstraintType::Lt => {
1148 self.arith.assert_lt(&terms, constant, reason);
1150 }
1151 ArithConstraintType::Le => {
1152 self.arith.assert_le(&terms, constant, reason);
1154 }
1155 ArithConstraintType::Gt => {
1156 self.arith.assert_gt(&terms, constant, reason);
1158 }
1159 ArithConstraintType::Ge => {
1160 self.arith.assert_ge(&terms, constant, reason);
1162 }
1163 }
1164 } else {
1165 match parsed.constraint_type {
1171 ArithConstraintType::Lt => {
1172 self.arith.assert_ge(&terms, constant, reason);
1174 }
1175 ArithConstraintType::Le => {
1176 self.arith.assert_gt(&terms, constant, reason);
1178 }
1179 ArithConstraintType::Gt => {
1180 self.arith.assert_le(&terms, constant, reason);
1182 }
1183 ArithConstraintType::Ge => {
1184 self.arith.assert_lt(&terms, constant, reason);
1186 }
1187 }
1188 }
1189
1190 use oxiz_theories::Theory;
1192 use oxiz_theories::TheoryCheckResult as TheoryCheckResultEnum;
1193 if let Ok(TheoryCheckResultEnum::Unsat(conflict_terms)) = self.arith.check() {
1194 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1195 return TheoryCheckResult::Conflict(conflict_lits);
1196 }
1197 }
1198 }
1199 }
1200 TheoryCheckResult::Sat
1201 }
1202}
1203
1204impl TheoryCallback for TheoryManager<'_> {
1205 fn on_assignment(&mut self, lit: Lit) -> TheoryCheckResult {
1206 let var = lit.var();
1207 let is_positive = !lit.is_neg();
1208
1209 self.statistics.propagations += 1;
1211
1212 if self.theory_mode == TheoryMode::Lazy {
1214 if self.var_to_constraint.contains_key(&var) {
1216 self.pending_assignments.push((lit, is_positive));
1217 }
1218 return TheoryCheckResult::Sat;
1219 }
1220
1221 let Some(constraint) = self.var_to_constraint.get(&var).cloned() else {
1224 return TheoryCheckResult::Sat;
1225 };
1226
1227 self.processed_count += 1;
1228 self.statistics.theory_propagations += 1;
1229
1230 let result = self.process_constraint(var, constraint, is_positive);
1231
1232 if matches!(result, TheoryCheckResult::Conflict(_)) {
1234 self.statistics.theory_conflicts += 1;
1235 self.statistics.conflicts += 1;
1236
1237 if self.max_conflicts > 0 && self.statistics.conflicts >= self.max_conflicts {
1239 return TheoryCheckResult::Sat; }
1241 }
1242
1243 result
1244 }
1245
1246 fn final_check(&mut self) -> TheoryCheckResult {
1247 if self.theory_mode == TheoryMode::Lazy {
1249 for &(lit, is_positive) in &self.pending_assignments.clone() {
1250 let var = lit.var();
1251 let Some(constraint) = self.var_to_constraint.get(&var).cloned() else {
1252 continue;
1253 };
1254
1255 self.statistics.theory_propagations += 1;
1256
1257 let result = self.process_constraint(var, constraint, is_positive);
1259 if let TheoryCheckResult::Conflict(conflict) = result {
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 return TheoryCheckResult::Conflict(conflict);
1269 }
1270 }
1271 self.pending_assignments.clear();
1273 }
1274
1275 if let Some(conflict_terms) = self.euf.check_conflicts() {
1277 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1279 self.statistics.theory_conflicts += 1;
1280 self.statistics.conflicts += 1;
1281
1282 if self.max_conflicts > 0 && self.statistics.conflicts >= self.max_conflicts {
1284 return TheoryCheckResult::Sat; }
1286
1287 return TheoryCheckResult::Conflict(conflict_lits);
1288 }
1289
1290 match self.arith.check() {
1292 Ok(result) => {
1293 match result {
1294 oxiz_theories::TheoryCheckResult::Sat => {
1295 self.model_based_combination()
1298 }
1299 oxiz_theories::TheoryCheckResult::Unsat(conflict_terms) => {
1300 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1302 self.statistics.theory_conflicts += 1;
1303 self.statistics.conflicts += 1;
1304
1305 if self.max_conflicts > 0 && self.statistics.conflicts >= self.max_conflicts
1307 {
1308 return TheoryCheckResult::Sat; }
1310
1311 TheoryCheckResult::Conflict(conflict_lits)
1312 }
1313 oxiz_theories::TheoryCheckResult::Propagate(_) => {
1314 self.model_based_combination()
1316 }
1317 oxiz_theories::TheoryCheckResult::Unknown => {
1318 TheoryCheckResult::Sat
1320 }
1321 }
1322 }
1323 Err(_error) => {
1324 TheoryCheckResult::Sat
1327 }
1328 }
1329 }
1330
1331 fn on_new_level(&mut self, level: u32) {
1332 while self.level_stack.len() < (level as usize + 1) {
1335 self.level_stack.push(self.processed_count);
1336 self.euf.push();
1337 self.arith.push();
1338 self.bv.push();
1339 }
1340 }
1341
1342 fn on_backtrack(&mut self, level: u32) {
1343 while self.level_stack.len() > (level as usize + 1) {
1345 self.level_stack.pop();
1346 self.euf.pop();
1347 self.arith.pop();
1348 self.bv.pop();
1349 }
1350 self.processed_count = *self.level_stack.last().unwrap_or(&0);
1351
1352 if self.theory_mode == TheoryMode::Lazy {
1354 self.pending_assignments.clear();
1355 }
1356 }
1357}
1358
1359#[derive(Debug, Clone)]
1361enum TrailOp {
1362 AssertionAdded { index: usize },
1364 VarCreated {
1366 #[allow(dead_code)]
1367 var: Var,
1368 term: TermId,
1369 },
1370 ConstraintAdded { var: Var },
1372 FalseAssertionSet,
1374 NamedAssertionAdded { index: usize },
1376}
1377
1378#[derive(Debug, Clone)]
1380struct ContextState {
1381 num_assertions: usize,
1382 num_vars: usize,
1383 has_false_assertion: bool,
1384 trail_position: usize,
1386}
1387
1388impl Default for Solver {
1389 fn default() -> Self {
1390 Self::new()
1391 }
1392}
1393
1394impl Solver {
1395 #[must_use]
1397 pub fn new() -> Self {
1398 Self::with_config(SolverConfig::default())
1399 }
1400
1401 #[must_use]
1403 pub fn with_config(config: SolverConfig) -> Self {
1404 let proof_enabled = config.proof;
1405
1406 let sat_config = SatConfig {
1408 restart_strategy: config.restart_strategy,
1409 enable_inprocessing: config.enable_inprocessing,
1410 inprocessing_interval: config.inprocessing_interval,
1411 ..SatConfig::default()
1412 };
1413
1414 Self {
1424 config,
1425 sat: SatSolver::with_config(sat_config),
1426 euf: EufSolver::new(),
1427 arith: ArithSolver::lra(),
1428 bv: BvSolver::new(),
1429 mbqi: MBQISolver::new(),
1430 has_quantifiers: false,
1431 term_to_var: FxHashMap::default(),
1432 var_to_term: Vec::new(),
1433 var_to_constraint: FxHashMap::default(),
1434 var_to_parsed_arith: FxHashMap::default(),
1435 logic: None,
1436 assertions: Vec::new(),
1437 named_assertions: Vec::new(),
1438 assumption_vars: FxHashMap::default(),
1439 model: None,
1440 unsat_core: None,
1441 context_stack: Vec::new(),
1442 trail: Vec::new(),
1443 theory_processed_up_to: 0,
1444 produce_unsat_cores: false,
1445 has_false_assertion: false,
1446 polarities: FxHashMap::default(),
1447 polarity_aware: true, theory_aware_branching: true, proof: if proof_enabled {
1450 Some(Proof::new())
1451 } else {
1452 None
1453 },
1454 simplifier: Simplifier::new(),
1455 statistics: Statistics::new(),
1456 }
1457 }
1458
1459 #[must_use]
1461 pub fn get_proof(&self) -> Option<&Proof> {
1462 self.proof.as_ref()
1463 }
1464
1465 #[must_use]
1467 pub fn get_statistics(&self) -> &Statistics {
1468 &self.statistics
1469 }
1470
1471 pub fn reset_statistics(&mut self) {
1473 self.statistics.reset();
1474 }
1475
1476 pub fn set_theory_aware_branching(&mut self, enabled: bool) {
1478 self.theory_aware_branching = enabled;
1479 }
1480
1481 #[must_use]
1483 pub fn theory_aware_branching(&self) -> bool {
1484 self.theory_aware_branching
1485 }
1486
1487 pub fn set_produce_unsat_cores(&mut self, produce: bool) {
1489 self.produce_unsat_cores = produce;
1490 }
1491
1492 pub fn set_logic(&mut self, logic: &str) {
1494 self.logic = Some(logic.to_string());
1495 }
1496
1497 fn collect_polarities(&mut self, term: TermId, polarity: Polarity, manager: &TermManager) {
1500 let current = self.polarities.get(&term).copied();
1502 let new_polarity = match (current, polarity) {
1503 (Some(Polarity::Both), _) | (_, Polarity::Both) => Polarity::Both,
1504 (Some(Polarity::Positive), Polarity::Negative)
1505 | (Some(Polarity::Negative), Polarity::Positive) => Polarity::Both,
1506 (Some(p), _) => p,
1507 (None, p) => p,
1508 };
1509 self.polarities.insert(term, new_polarity);
1510
1511 if current == Some(Polarity::Both) {
1513 return;
1514 }
1515
1516 let Some(t) = manager.get(term) else {
1518 return;
1519 };
1520
1521 match &t.kind {
1522 TermKind::Not(arg) => {
1523 let neg_polarity = match polarity {
1524 Polarity::Positive => Polarity::Negative,
1525 Polarity::Negative => Polarity::Positive,
1526 Polarity::Both => Polarity::Both,
1527 };
1528 self.collect_polarities(*arg, neg_polarity, manager);
1529 }
1530 TermKind::And(args) | TermKind::Or(args) => {
1531 for &arg in args {
1532 self.collect_polarities(arg, polarity, manager);
1533 }
1534 }
1535 TermKind::Implies(lhs, rhs) => {
1536 let neg_polarity = match polarity {
1537 Polarity::Positive => Polarity::Negative,
1538 Polarity::Negative => Polarity::Positive,
1539 Polarity::Both => Polarity::Both,
1540 };
1541 self.collect_polarities(*lhs, neg_polarity, manager);
1542 self.collect_polarities(*rhs, polarity, manager);
1543 }
1544 TermKind::Ite(cond, then_br, else_br) => {
1545 self.collect_polarities(*cond, Polarity::Both, manager);
1546 self.collect_polarities(*then_br, polarity, manager);
1547 self.collect_polarities(*else_br, polarity, manager);
1548 }
1549 TermKind::Xor(lhs, rhs) | TermKind::Eq(lhs, rhs) => {
1550 self.collect_polarities(*lhs, Polarity::Both, manager);
1552 self.collect_polarities(*rhs, Polarity::Both, manager);
1553 }
1554 _ => {
1555 }
1557 }
1558 }
1559
1560 fn get_or_create_var(&mut self, term: TermId) -> Var {
1562 if let Some(&var) = self.term_to_var.get(&term) {
1563 return var;
1564 }
1565
1566 let var = self.sat.new_var();
1567 self.term_to_var.insert(term, var);
1568 self.trail.push(TrailOp::VarCreated { var, term });
1569
1570 while self.var_to_term.len() <= var.index() {
1571 self.var_to_term.push(TermId::new(0));
1572 }
1573 self.var_to_term[var.index()] = term;
1574 var
1575 }
1576
1577 fn parse_arith_comparison(
1580 &self,
1581 lhs: TermId,
1582 rhs: TermId,
1583 constraint_type: ArithConstraintType,
1584 reason: TermId,
1585 manager: &TermManager,
1586 ) -> Option<ParsedArithConstraint> {
1587 let mut terms: SmallVec<[(TermId, Rational64); 4]> = SmallVec::new();
1588 let mut constant = Rational64::zero();
1589
1590 self.extract_linear_terms(lhs, Rational64::one(), &mut terms, &mut constant, manager)?;
1592
1593 self.extract_linear_terms(rhs, -Rational64::one(), &mut terms, &mut constant, manager)?;
1596
1597 let mut combined: FxHashMap<TermId, Rational64> = FxHashMap::default();
1599 for (term, coef) in terms {
1600 *combined.entry(term).or_insert(Rational64::zero()) += coef;
1601 }
1602
1603 let final_terms: SmallVec<[(TermId, Rational64); 4]> =
1605 combined.into_iter().filter(|(_, c)| !c.is_zero()).collect();
1606
1607 Some(ParsedArithConstraint {
1608 terms: final_terms,
1609 constant: -constant, constraint_type,
1611 reason_term: reason,
1612 })
1613 }
1614
1615 fn extract_linear_terms(
1618 &self,
1619 term_id: TermId,
1620 scale: Rational64,
1621 terms: &mut SmallVec<[(TermId, Rational64); 4]>,
1622 constant: &mut Rational64,
1623 manager: &TermManager,
1624 ) -> Option<()> {
1625 let term = manager.get(term_id)?;
1626
1627 match &term.kind {
1628 TermKind::IntConst(n) => {
1630 if let Some(val) = n.to_i64() {
1631 *constant += scale * Rational64::from_integer(val);
1632 Some(())
1633 } else {
1634 None
1636 }
1637 }
1638
1639 TermKind::RealConst(r) => {
1641 *constant += scale * *r;
1642 Some(())
1643 }
1644
1645 TermKind::BitVecConst { value, .. } => {
1647 if let Some(val) = value.to_i64() {
1648 *constant += scale * Rational64::from_integer(val);
1649 Some(())
1650 } else {
1651 None
1653 }
1654 }
1655
1656 TermKind::Var(_) => {
1658 terms.push((term_id, scale));
1659 Some(())
1660 }
1661
1662 TermKind::Add(args) => {
1664 for &arg in args {
1665 self.extract_linear_terms(arg, scale, terms, constant, manager)?;
1666 }
1667 Some(())
1668 }
1669
1670 TermKind::Sub(lhs, rhs) => {
1672 self.extract_linear_terms(*lhs, scale, terms, constant, manager)?;
1673 self.extract_linear_terms(*rhs, -scale, terms, constant, manager)?;
1674 Some(())
1675 }
1676
1677 TermKind::Neg(arg) => self.extract_linear_terms(*arg, -scale, terms, constant, manager),
1679
1680 TermKind::Mul(args) => {
1682 let mut const_product = Rational64::one();
1684 let mut var_term: Option<TermId> = None;
1685
1686 for &arg in args {
1687 let arg_term = manager.get(arg)?;
1688 match &arg_term.kind {
1689 TermKind::IntConst(n) => {
1690 if let Some(val) = n.to_i64() {
1691 const_product *= Rational64::from_integer(val);
1692 } else {
1693 return None; }
1695 }
1696 TermKind::RealConst(r) => {
1697 const_product *= *r;
1698 }
1699 _ => {
1700 if var_term.is_some() {
1701 return None;
1703 }
1704 var_term = Some(arg);
1705 }
1706 }
1707 }
1708
1709 let new_scale = scale * const_product;
1710 match var_term {
1711 Some(v) => self.extract_linear_terms(v, new_scale, terms, constant, manager),
1712 None => {
1713 *constant += new_scale;
1715 Some(())
1716 }
1717 }
1718 }
1719
1720 _ => None,
1722 }
1723 }
1724
1725 pub fn assert(&mut self, term: TermId, manager: &mut TermManager) {
1727 let index = self.assertions.len();
1728 self.assertions.push(term);
1729 self.trail.push(TrailOp::AssertionAdded { index });
1730
1731 if let Some(t) = manager.get(term) {
1733 match t.kind {
1734 TermKind::False => {
1735 if !self.has_false_assertion {
1737 self.has_false_assertion = true;
1738 self.trail.push(TrailOp::FalseAssertionSet);
1739 }
1740 if self.produce_unsat_cores {
1741 let na_index = self.named_assertions.len();
1742 self.named_assertions.push(NamedAssertion {
1743 term,
1744 name: None,
1745 index: index as u32,
1746 });
1747 self.trail
1748 .push(TrailOp::NamedAssertionAdded { index: na_index });
1749 }
1750 return;
1751 }
1752 TermKind::True => {
1753 if self.produce_unsat_cores {
1755 let na_index = self.named_assertions.len();
1756 self.named_assertions.push(NamedAssertion {
1757 term,
1758 name: None,
1759 index: index as u32,
1760 });
1761 self.trail
1762 .push(TrailOp::NamedAssertionAdded { index: na_index });
1763 }
1764 return;
1765 }
1766 _ => {}
1767 }
1768 }
1769
1770 let term_to_encode = if self.config.simplify {
1772 self.simplifier.simplify(term, manager)
1773 } else {
1774 term
1775 };
1776
1777 if let Some(t) = manager.get(term_to_encode) {
1779 match t.kind {
1780 TermKind::False => {
1781 if !self.has_false_assertion {
1782 self.has_false_assertion = true;
1783 self.trail.push(TrailOp::FalseAssertionSet);
1784 }
1785 return;
1786 }
1787 TermKind::True => {
1788 return;
1790 }
1791 _ => {}
1792 }
1793 }
1794
1795 if self.polarity_aware {
1797 self.collect_polarities(term_to_encode, Polarity::Positive, manager);
1798 }
1799
1800 let lit = self.encode(term_to_encode, manager);
1802 self.sat.add_clause([lit]);
1803
1804 if self.produce_unsat_cores {
1805 let na_index = self.named_assertions.len();
1806 self.named_assertions.push(NamedAssertion {
1807 term,
1808 name: None,
1809 index: index as u32,
1810 });
1811 self.trail
1812 .push(TrailOp::NamedAssertionAdded { index: na_index });
1813 }
1814 }
1815
1816 pub fn assert_named(&mut self, term: TermId, name: &str, manager: &mut TermManager) {
1818 let index = self.assertions.len();
1819 self.assertions.push(term);
1820 self.trail.push(TrailOp::AssertionAdded { index });
1821
1822 if let Some(t) = manager.get(term) {
1824 match t.kind {
1825 TermKind::False => {
1826 if !self.has_false_assertion {
1828 self.has_false_assertion = true;
1829 self.trail.push(TrailOp::FalseAssertionSet);
1830 }
1831 if self.produce_unsat_cores {
1832 let na_index = self.named_assertions.len();
1833 self.named_assertions.push(NamedAssertion {
1834 term,
1835 name: Some(name.to_string()),
1836 index: index as u32,
1837 });
1838 self.trail
1839 .push(TrailOp::NamedAssertionAdded { index: na_index });
1840 }
1841 return;
1842 }
1843 TermKind::True => {
1844 if self.produce_unsat_cores {
1846 let na_index = self.named_assertions.len();
1847 self.named_assertions.push(NamedAssertion {
1848 term,
1849 name: Some(name.to_string()),
1850 index: index as u32,
1851 });
1852 self.trail
1853 .push(TrailOp::NamedAssertionAdded { index: na_index });
1854 }
1855 return;
1856 }
1857 _ => {}
1858 }
1859 }
1860
1861 if self.polarity_aware {
1863 self.collect_polarities(term, Polarity::Positive, manager);
1864 }
1865
1866 let lit = self.encode(term, manager);
1868 self.sat.add_clause([lit]);
1869
1870 if self.produce_unsat_cores {
1871 let na_index = self.named_assertions.len();
1872 self.named_assertions.push(NamedAssertion {
1873 term,
1874 name: Some(name.to_string()),
1875 index: index as u32,
1876 });
1877 self.trail
1878 .push(TrailOp::NamedAssertionAdded { index: na_index });
1879 }
1880 }
1881
1882 #[must_use]
1884 pub fn get_unsat_core(&self) -> Option<&UnsatCore> {
1885 self.unsat_core.as_ref()
1886 }
1887
1888 fn encode(&mut self, term: TermId, manager: &mut TermManager) -> Lit {
1890 let Some(t) = manager.get(term).cloned() else {
1892 let var = self.get_or_create_var(term);
1893 return Lit::pos(var);
1894 };
1895
1896 match &t.kind {
1897 TermKind::True => {
1898 let var = self.get_or_create_var(manager.mk_true());
1899 self.sat.add_clause([Lit::pos(var)]);
1900 Lit::pos(var)
1901 }
1902 TermKind::False => {
1903 let var = self.get_or_create_var(manager.mk_false());
1904 self.sat.add_clause([Lit::neg(var)]);
1905 Lit::neg(var)
1906 }
1907 TermKind::Var(_) => {
1908 let var = self.get_or_create_var(term);
1909 Lit::pos(var)
1910 }
1911 TermKind::Not(arg) => {
1912 let arg_lit = self.encode(*arg, manager);
1913 arg_lit.negate()
1914 }
1915 TermKind::And(args) => {
1916 let result_var = self.get_or_create_var(term);
1917 let result = Lit::pos(result_var);
1918
1919 let mut arg_lits: Vec<Lit> = Vec::new();
1920 for &arg in args {
1921 arg_lits.push(self.encode(arg, manager));
1922 }
1923
1924 let polarity = if self.polarity_aware {
1926 self.polarities
1927 .get(&term)
1928 .copied()
1929 .unwrap_or(Polarity::Both)
1930 } else {
1931 Polarity::Both
1932 };
1933
1934 if polarity != Polarity::Negative {
1937 for &arg in &arg_lits {
1938 self.sat.add_clause([result.negate(), arg]);
1939 }
1940 }
1941
1942 if polarity != Polarity::Positive {
1945 let mut clause: Vec<Lit> = arg_lits.iter().map(|l| l.negate()).collect();
1946 clause.push(result);
1947 self.sat.add_clause(clause);
1948 }
1949
1950 result
1951 }
1952 TermKind::Or(args) => {
1953 let result_var = self.get_or_create_var(term);
1954 let result = Lit::pos(result_var);
1955
1956 let mut arg_lits: Vec<Lit> = Vec::new();
1957 for &arg in args {
1958 arg_lits.push(self.encode(arg, manager));
1959 }
1960
1961 let polarity = if self.polarity_aware {
1963 self.polarities
1964 .get(&term)
1965 .copied()
1966 .unwrap_or(Polarity::Both)
1967 } else {
1968 Polarity::Both
1969 };
1970
1971 if polarity != Polarity::Negative {
1974 let mut clause: Vec<Lit> = vec![result.negate()];
1975 clause.extend(arg_lits.iter().copied());
1976 self.sat.add_clause(clause);
1977 }
1978
1979 if polarity != Polarity::Positive {
1982 for &arg in &arg_lits {
1983 self.sat.add_clause([arg.negate(), result]);
1984 }
1985 }
1986
1987 result
1988 }
1989 TermKind::Xor(lhs, rhs) => {
1990 let lhs_lit = self.encode(*lhs, manager);
1991 let rhs_lit = self.encode(*rhs, manager);
1992
1993 let result_var = self.get_or_create_var(term);
1994 let result = Lit::pos(result_var);
1995
1996 self.sat.add_clause([result.negate(), lhs_lit, rhs_lit]);
2001 self.sat
2003 .add_clause([result.negate(), lhs_lit.negate(), rhs_lit.negate()]);
2004
2005 self.sat.add_clause([lhs_lit.negate(), rhs_lit, result]);
2007 self.sat.add_clause([lhs_lit, rhs_lit.negate(), result]);
2009
2010 result
2011 }
2012 TermKind::Implies(lhs, rhs) => {
2013 let lhs_lit = self.encode(*lhs, manager);
2014 let rhs_lit = self.encode(*rhs, manager);
2015
2016 let result_var = self.get_or_create_var(term);
2017 let result = Lit::pos(result_var);
2018
2019 self.sat
2022 .add_clause([result.negate(), lhs_lit.negate(), rhs_lit]);
2023
2024 self.sat.add_clause([lhs_lit, result]);
2027 self.sat.add_clause([rhs_lit.negate(), result]);
2028
2029 result
2030 }
2031 TermKind::Ite(cond, then_br, else_br) => {
2032 let cond_lit = self.encode(*cond, manager);
2033 let then_lit = self.encode(*then_br, manager);
2034 let else_lit = self.encode(*else_br, manager);
2035
2036 let result_var = self.get_or_create_var(term);
2037 let result = Lit::pos(result_var);
2038
2039 self.sat
2042 .add_clause([cond_lit.negate(), result.negate(), then_lit]);
2043 self.sat
2045 .add_clause([cond_lit.negate(), then_lit.negate(), result]);
2046
2047 self.sat.add_clause([cond_lit, result.negate(), else_lit]);
2049 self.sat.add_clause([cond_lit, else_lit.negate(), result]);
2051
2052 result
2053 }
2054 TermKind::Eq(lhs, rhs) => {
2055 let lhs_term = manager.get(*lhs);
2057 let is_bool_eq = lhs_term.is_some_and(|t| t.sort == manager.sorts.bool_sort);
2058
2059 if is_bool_eq {
2060 let lhs_lit = self.encode(*lhs, manager);
2062 let rhs_lit = self.encode(*rhs, manager);
2063
2064 let result_var = self.get_or_create_var(term);
2065 let result = Lit::pos(result_var);
2066
2067 self.sat
2070 .add_clause([result.negate(), lhs_lit.negate(), rhs_lit]);
2071 self.sat
2072 .add_clause([result.negate(), rhs_lit.negate(), lhs_lit]);
2073
2074 self.sat.add_clause([lhs_lit, rhs_lit, result]);
2076 self.sat
2077 .add_clause([lhs_lit.negate(), rhs_lit.negate(), result]);
2078
2079 result
2080 } else {
2081 let var = self.get_or_create_var(term);
2084 self.var_to_constraint
2085 .insert(var, Constraint::Eq(*lhs, *rhs));
2086 self.trail.push(TrailOp::ConstraintAdded { var });
2087 Lit::pos(var)
2088 }
2089 }
2090 TermKind::Distinct(args) => {
2091 if args.len() <= 1 {
2094 let var = self.get_or_create_var(manager.mk_true());
2096 return Lit::pos(var);
2097 }
2098
2099 let result_var = self.get_or_create_var(term);
2100 let result = Lit::pos(result_var);
2101
2102 let mut diseq_lits = Vec::new();
2103 for i in 0..args.len() {
2104 for j in (i + 1)..args.len() {
2105 let eq = manager.mk_eq(args[i], args[j]);
2106 let eq_lit = self.encode(eq, manager);
2107 diseq_lits.push(eq_lit.negate());
2108 }
2109 }
2110
2111 for &diseq in &diseq_lits {
2113 self.sat.add_clause([result.negate(), diseq]);
2114 }
2115
2116 let mut clause: Vec<Lit> = diseq_lits.iter().map(|l| l.negate()).collect();
2118 clause.push(result);
2119 self.sat.add_clause(clause);
2120
2121 result
2122 }
2123 TermKind::Let { bindings, body } => {
2124 let substituted = *body;
2128 for (name, value) in bindings.iter().rev() {
2129 let _ = (name, value);
2132 }
2133 self.encode(substituted, manager)
2134 }
2135 TermKind::IntConst(_) | TermKind::RealConst(_) | TermKind::BitVecConst { .. } => {
2138 let var = self.get_or_create_var(term);
2141 Lit::pos(var)
2142 }
2143 TermKind::Neg(_)
2144 | TermKind::Add(_)
2145 | TermKind::Sub(_, _)
2146 | TermKind::Mul(_)
2147 | TermKind::Div(_, _)
2148 | TermKind::Mod(_, _) => {
2149 let var = self.get_or_create_var(term);
2151 Lit::pos(var)
2152 }
2153 TermKind::Lt(lhs, rhs) => {
2154 let var = self.get_or_create_var(term);
2156 self.var_to_constraint
2157 .insert(var, Constraint::Lt(*lhs, *rhs));
2158 self.trail.push(TrailOp::ConstraintAdded { var });
2159 if let Some(parsed) =
2161 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Lt, term, manager)
2162 {
2163 self.var_to_parsed_arith.insert(var, parsed);
2164 }
2165 Lit::pos(var)
2166 }
2167 TermKind::Le(lhs, rhs) => {
2168 let var = self.get_or_create_var(term);
2170 self.var_to_constraint
2171 .insert(var, Constraint::Le(*lhs, *rhs));
2172 self.trail.push(TrailOp::ConstraintAdded { var });
2173 if let Some(parsed) =
2175 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Le, term, manager)
2176 {
2177 self.var_to_parsed_arith.insert(var, parsed);
2178 }
2179 Lit::pos(var)
2180 }
2181 TermKind::Gt(lhs, rhs) => {
2182 let var = self.get_or_create_var(term);
2184 self.var_to_constraint
2185 .insert(var, Constraint::Gt(*lhs, *rhs));
2186 self.trail.push(TrailOp::ConstraintAdded { var });
2187 if let Some(parsed) =
2189 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Gt, term, manager)
2190 {
2191 self.var_to_parsed_arith.insert(var, parsed);
2192 }
2193 Lit::pos(var)
2194 }
2195 TermKind::Ge(lhs, rhs) => {
2196 let var = self.get_or_create_var(term);
2198 self.var_to_constraint
2199 .insert(var, Constraint::Ge(*lhs, *rhs));
2200 self.trail.push(TrailOp::ConstraintAdded { var });
2201 if let Some(parsed) =
2203 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Ge, term, manager)
2204 {
2205 self.var_to_parsed_arith.insert(var, parsed);
2206 }
2207 Lit::pos(var)
2208 }
2209 TermKind::BvConcat(_, _)
2210 | TermKind::BvExtract { .. }
2211 | TermKind::BvNot(_)
2212 | TermKind::BvAnd(_, _)
2213 | TermKind::BvOr(_, _)
2214 | TermKind::BvXor(_, _)
2215 | TermKind::BvAdd(_, _)
2216 | TermKind::BvSub(_, _)
2217 | TermKind::BvMul(_, _)
2218 | TermKind::BvUdiv(_, _)
2219 | TermKind::BvSdiv(_, _)
2220 | TermKind::BvUrem(_, _)
2221 | TermKind::BvSrem(_, _)
2222 | TermKind::BvShl(_, _)
2223 | TermKind::BvLshr(_, _)
2224 | TermKind::BvAshr(_, _) => {
2225 let var = self.get_or_create_var(term);
2227 Lit::pos(var)
2228 }
2229 TermKind::BvUlt(lhs, rhs) => {
2230 let var = self.get_or_create_var(term);
2232 self.var_to_constraint
2233 .insert(var, Constraint::Lt(*lhs, *rhs));
2234 self.trail.push(TrailOp::ConstraintAdded { var });
2235 if let Some(parsed) =
2237 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Lt, term, manager)
2238 {
2239 self.var_to_parsed_arith.insert(var, parsed);
2240 }
2241 Lit::pos(var)
2242 }
2243 TermKind::BvUle(lhs, rhs) => {
2244 let var = self.get_or_create_var(term);
2246 self.var_to_constraint
2247 .insert(var, Constraint::Le(*lhs, *rhs));
2248 self.trail.push(TrailOp::ConstraintAdded { var });
2249 if let Some(parsed) =
2250 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Le, term, manager)
2251 {
2252 self.var_to_parsed_arith.insert(var, parsed);
2253 }
2254 Lit::pos(var)
2255 }
2256 TermKind::BvSlt(lhs, rhs) => {
2257 let var = self.get_or_create_var(term);
2259 self.var_to_constraint
2260 .insert(var, Constraint::Lt(*lhs, *rhs));
2261 self.trail.push(TrailOp::ConstraintAdded { var });
2262 if let Some(parsed) =
2263 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Lt, term, manager)
2264 {
2265 self.var_to_parsed_arith.insert(var, parsed);
2266 }
2267 Lit::pos(var)
2268 }
2269 TermKind::BvSle(lhs, rhs) => {
2270 let var = self.get_or_create_var(term);
2272 self.var_to_constraint
2273 .insert(var, Constraint::Le(*lhs, *rhs));
2274 self.trail.push(TrailOp::ConstraintAdded { var });
2275 if let Some(parsed) =
2276 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Le, term, manager)
2277 {
2278 self.var_to_parsed_arith.insert(var, parsed);
2279 }
2280 Lit::pos(var)
2281 }
2282 TermKind::Select(_, _) | TermKind::Store(_, _, _) => {
2283 let var = self.get_or_create_var(term);
2285 Lit::pos(var)
2286 }
2287 TermKind::Apply { .. } => {
2288 let var = self.get_or_create_var(term);
2290 Lit::pos(var)
2291 }
2292 TermKind::Forall { patterns, .. } => {
2293 self.has_quantifiers = true;
2295 self.mbqi.add_quantifier(term, manager);
2296 for pattern in patterns {
2298 for &trigger in pattern {
2299 self.mbqi.collect_ground_terms(trigger, manager);
2300 }
2301 }
2302 let var = self.get_or_create_var(term);
2304 Lit::pos(var)
2305 }
2306 TermKind::Exists { patterns, .. } => {
2307 self.has_quantifiers = true;
2309 self.mbqi.add_quantifier(term, manager);
2310 for pattern in patterns {
2312 for &trigger in pattern {
2313 self.mbqi.collect_ground_terms(trigger, manager);
2314 }
2315 }
2316 let var = self.get_or_create_var(term);
2318 Lit::pos(var)
2319 }
2320 TermKind::StringLit(_)
2322 | TermKind::StrConcat(_, _)
2323 | TermKind::StrLen(_)
2324 | TermKind::StrSubstr(_, _, _)
2325 | TermKind::StrAt(_, _)
2326 | TermKind::StrReplace(_, _, _)
2327 | TermKind::StrReplaceAll(_, _, _)
2328 | TermKind::StrToInt(_)
2329 | TermKind::IntToStr(_)
2330 | TermKind::StrInRe(_, _) => {
2331 let var = self.get_or_create_var(term);
2333 Lit::pos(var)
2334 }
2335 TermKind::StrContains(_, _)
2336 | TermKind::StrPrefixOf(_, _)
2337 | TermKind::StrSuffixOf(_, _)
2338 | TermKind::StrIndexOf(_, _, _) => {
2339 let var = self.get_or_create_var(term);
2341 Lit::pos(var)
2342 }
2343 TermKind::FpLit { .. }
2345 | TermKind::FpPlusInfinity { .. }
2346 | TermKind::FpMinusInfinity { .. }
2347 | TermKind::FpPlusZero { .. }
2348 | TermKind::FpMinusZero { .. }
2349 | TermKind::FpNaN { .. } => {
2350 let var = self.get_or_create_var(term);
2352 Lit::pos(var)
2353 }
2354 TermKind::FpAbs(_)
2356 | TermKind::FpNeg(_)
2357 | TermKind::FpSqrt(_, _)
2358 | TermKind::FpRoundToIntegral(_, _)
2359 | TermKind::FpAdd(_, _, _)
2360 | TermKind::FpSub(_, _, _)
2361 | TermKind::FpMul(_, _, _)
2362 | TermKind::FpDiv(_, _, _)
2363 | TermKind::FpRem(_, _)
2364 | TermKind::FpMin(_, _)
2365 | TermKind::FpMax(_, _)
2366 | TermKind::FpFma(_, _, _, _) => {
2367 let var = self.get_or_create_var(term);
2369 Lit::pos(var)
2370 }
2371 TermKind::FpLeq(_, _)
2373 | TermKind::FpLt(_, _)
2374 | TermKind::FpGeq(_, _)
2375 | TermKind::FpGt(_, _)
2376 | TermKind::FpEq(_, _)
2377 | TermKind::FpIsNormal(_)
2378 | TermKind::FpIsSubnormal(_)
2379 | TermKind::FpIsZero(_)
2380 | TermKind::FpIsInfinite(_)
2381 | TermKind::FpIsNaN(_)
2382 | TermKind::FpIsNegative(_)
2383 | TermKind::FpIsPositive(_) => {
2384 let var = self.get_or_create_var(term);
2386 Lit::pos(var)
2387 }
2388 TermKind::FpToFp { .. }
2390 | TermKind::FpToSBV { .. }
2391 | TermKind::FpToUBV { .. }
2392 | TermKind::FpToReal(_)
2393 | TermKind::RealToFp { .. }
2394 | TermKind::SBVToFp { .. }
2395 | TermKind::UBVToFp { .. } => {
2396 let var = self.get_or_create_var(term);
2398 Lit::pos(var)
2399 }
2400 TermKind::DtConstructor { .. }
2402 | TermKind::DtTester { .. }
2403 | TermKind::DtSelector { .. } => {
2404 let var = self.get_or_create_var(term);
2406 Lit::pos(var)
2407 }
2408 }
2409 }
2410
2411 pub fn check(&mut self, manager: &mut TermManager) -> SolverResult {
2413 if self.has_false_assertion {
2415 self.build_unsat_core_trivial_false();
2416 return SolverResult::Unsat;
2417 }
2418
2419 if self.assertions.is_empty() {
2420 return SolverResult::Sat;
2421 }
2422
2423 if self.config.max_conflicts > 0 && self.statistics.conflicts >= self.config.max_conflicts {
2425 return SolverResult::Unknown;
2426 }
2427 if self.config.max_decisions > 0 && self.statistics.decisions >= self.config.max_decisions {
2428 return SolverResult::Unknown;
2429 }
2430
2431 let mut theory_manager = TheoryManager::new(
2433 &mut self.euf,
2434 &mut self.arith,
2435 &mut self.bv,
2436 &self.var_to_constraint,
2437 &self.var_to_parsed_arith,
2438 &self.term_to_var,
2439 self.config.theory_mode,
2440 &mut self.statistics,
2441 self.config.max_conflicts,
2442 self.config.max_decisions,
2443 );
2444
2445 let max_mbqi_iterations = 100;
2447 let mut mbqi_iteration = 0;
2448
2449 loop {
2450 let sat_result = self.sat.solve_with_theory(&mut theory_manager);
2451
2452 match sat_result {
2453 SatResult::Unsat => {
2454 self.build_unsat_core();
2455 return SolverResult::Unsat;
2456 }
2457 SatResult::Unknown => {
2458 return SolverResult::Unknown;
2459 }
2460 SatResult::Sat => {
2461 if !self.has_quantifiers {
2463 self.build_model(manager);
2464 self.unsat_core = None;
2465 return SolverResult::Sat;
2466 }
2467
2468 self.build_model(manager);
2470
2471 let model_assignments = self
2473 .model
2474 .as_ref()
2475 .map(|m| m.assignments().clone())
2476 .unwrap_or_default();
2477 let mbqi_result = self.mbqi.check_with_model(&model_assignments, manager);
2478
2479 match mbqi_result {
2480 MBQIResult::NoQuantifiers | MBQIResult::Satisfied => {
2481 self.unsat_core = None;
2483 return SolverResult::Sat;
2484 }
2485 MBQIResult::InstantiationLimit => {
2486 return SolverResult::Unknown;
2488 }
2489 MBQIResult::Conflict(conflict_terms) => {
2490 let lits: Vec<Lit> = conflict_terms
2492 .iter()
2493 .filter_map(|&t| self.term_to_var.get(&t).map(|&v| Lit::neg(v)))
2494 .collect();
2495 if !lits.is_empty() {
2496 self.sat.add_clause(lits);
2497 }
2498 }
2500 MBQIResult::NewInstantiations(instantiations) => {
2501 for inst in instantiations {
2503 let lit = self.encode(inst.result, manager);
2506 self.sat.add_clause([lit]);
2507 }
2508 }
2510 }
2511
2512 mbqi_iteration += 1;
2513 if mbqi_iteration >= max_mbqi_iterations {
2514 return SolverResult::Unknown;
2515 }
2516
2517 theory_manager = TheoryManager::new(
2519 &mut self.euf,
2520 &mut self.arith,
2521 &mut self.bv,
2522 &self.var_to_constraint,
2523 &self.var_to_parsed_arith,
2524 &self.term_to_var,
2525 self.config.theory_mode,
2526 &mut self.statistics,
2527 self.config.max_conflicts,
2528 self.config.max_decisions,
2529 );
2530 }
2531 }
2532 }
2533 }
2534
2535 pub fn check_with_assumptions(
2538 &mut self,
2539 assumptions: &[TermId],
2540 manager: &mut TermManager,
2541 ) -> SolverResult {
2542 self.push();
2544
2545 for &assumption in assumptions {
2547 self.assert(assumption, manager);
2548 }
2549
2550 let result = self.check(manager);
2552
2553 self.pop();
2555
2556 result
2557 }
2558
2559 pub fn check_sat_only(&mut self, manager: &mut TermManager) -> SolverResult {
2562 if self.assertions.is_empty() {
2563 return SolverResult::Sat;
2564 }
2565
2566 match self.sat.solve() {
2567 SatResult::Sat => {
2568 self.build_model(manager);
2569 SolverResult::Sat
2570 }
2571 SatResult::Unsat => SolverResult::Unsat,
2572 SatResult::Unknown => SolverResult::Unknown,
2573 }
2574 }
2575
2576 fn build_model(&mut self, manager: &mut TermManager) {
2578 let mut model = Model::new();
2579 let sat_model = self.sat.model();
2580
2581 for (&term, &var) in &self.term_to_var {
2583 let val = sat_model.get(var.index()).copied();
2584 if let Some(v) = val {
2585 let bool_val = if v.is_true() {
2586 manager.mk_true()
2587 } else if v.is_false() {
2588 manager.mk_false()
2589 } else {
2590 continue;
2591 };
2592 model.set(term, bool_val);
2593 }
2594 }
2595
2596 for &term in self.term_to_var.keys() {
2599 if let Some(value) = self.arith.value(term) {
2600 let value_term = if *value.denom() == 1 {
2602 manager.mk_int(*value.numer())
2604 } else {
2605 manager.mk_real(value)
2607 };
2608 model.set(term, value_term);
2609 }
2610 }
2611
2612 self.model = Some(model);
2613 }
2614
2615 fn build_unsat_core_trivial_false(&mut self) {
2617 if !self.produce_unsat_cores {
2618 self.unsat_core = None;
2619 return;
2620 }
2621
2622 let mut core = UnsatCore::new();
2624
2625 for (i, &term) in self.assertions.iter().enumerate() {
2626 if term == TermId::new(1) {
2627 core.indices.push(i as u32);
2629
2630 if let Some(named) = self.named_assertions.iter().find(|na| na.index == i as u32)
2632 && let Some(ref name) = named.name
2633 {
2634 core.names.push(name.clone());
2635 }
2636 }
2637 }
2638
2639 self.unsat_core = Some(core);
2640 }
2641
2642 fn build_unsat_core(&mut self) {
2644 if !self.produce_unsat_cores {
2645 self.unsat_core = None;
2646 return;
2647 }
2648
2649 let mut core = UnsatCore::new();
2654
2655 if !self.assumption_vars.is_empty() {
2657 for na in &self.named_assertions {
2662 core.indices.push(na.index);
2663 if let Some(ref name) = na.name {
2664 core.names.push(name.clone());
2665 }
2666 }
2667 } else {
2668 for na in &self.named_assertions {
2671 core.indices.push(na.index);
2672 if let Some(ref name) = na.name {
2673 core.names.push(name.clone());
2674 }
2675 }
2676 }
2677
2678 self.unsat_core = Some(core);
2679 }
2680
2681 pub fn enable_assumption_based_cores(&mut self) {
2685 self.produce_unsat_cores = true;
2686 }
2689
2690 pub fn minimize_unsat_core(&mut self, manager: &mut TermManager) -> Option<UnsatCore> {
2693 if !self.produce_unsat_cores {
2694 return None;
2695 }
2696
2697 let core = self.unsat_core.as_ref()?;
2699 if core.is_empty() {
2700 return Some(core.clone());
2701 }
2702
2703 let mut core_assertions: Vec<_> = core
2705 .indices
2706 .iter()
2707 .map(|&idx| {
2708 let assertion = self.assertions[idx as usize];
2709 let name = self
2710 .named_assertions
2711 .iter()
2712 .find(|na| na.index == idx)
2713 .and_then(|na| na.name.clone());
2714 (idx, assertion, name)
2715 })
2716 .collect();
2717
2718 let mut i = 0;
2720 while i < core_assertions.len() {
2721 let mut temp_solver = Solver::new();
2723 temp_solver.set_logic(self.logic.as_deref().unwrap_or("ALL"));
2724
2725 for (j, &(_, assertion, _)) in core_assertions.iter().enumerate() {
2727 if i != j {
2728 temp_solver.assert(assertion, manager);
2729 }
2730 }
2731
2732 if temp_solver.check(manager) == SolverResult::Unsat {
2734 core_assertions.remove(i);
2736 } else {
2738 i += 1;
2740 }
2741 }
2742
2743 let mut minimized = UnsatCore::new();
2745 for (idx, _, name) in core_assertions {
2746 minimized.indices.push(idx);
2747 if let Some(n) = name {
2748 minimized.names.push(n);
2749 }
2750 }
2751
2752 Some(minimized)
2753 }
2754
2755 #[must_use]
2757 pub fn model(&self) -> Option<&Model> {
2758 self.model.as_ref()
2759 }
2760
2761 pub fn assert_many(&mut self, terms: &[TermId], manager: &mut TermManager) {
2764 for &term in terms {
2765 self.assert(term, manager);
2766 }
2767 }
2768
2769 #[must_use]
2771 pub fn num_assertions(&self) -> usize {
2772 self.assertions.len()
2773 }
2774
2775 #[must_use]
2777 pub fn num_variables(&self) -> usize {
2778 self.term_to_var.len()
2779 }
2780
2781 #[must_use]
2783 pub fn has_assertions(&self) -> bool {
2784 !self.assertions.is_empty()
2785 }
2786
2787 #[must_use]
2789 pub fn context_level(&self) -> usize {
2790 self.context_stack.len()
2791 }
2792
2793 pub fn push(&mut self) {
2795 self.context_stack.push(ContextState {
2796 num_assertions: self.assertions.len(),
2797 num_vars: self.var_to_term.len(),
2798 has_false_assertion: self.has_false_assertion,
2799 trail_position: self.trail.len(),
2800 });
2801 self.sat.push();
2802 self.euf.push();
2803 self.arith.push();
2804 }
2805
2806 pub fn pop(&mut self) {
2808 if let Some(state) = self.context_stack.pop() {
2809 while self.trail.len() > state.trail_position {
2811 if let Some(op) = self.trail.pop() {
2812 match op {
2813 TrailOp::AssertionAdded { index } => {
2814 if self.assertions.len() > index {
2815 self.assertions.truncate(index);
2816 }
2817 }
2818 TrailOp::VarCreated { var: _, term } => {
2819 self.term_to_var.remove(&term);
2821 }
2822 TrailOp::ConstraintAdded { var } => {
2823 self.var_to_constraint.remove(&var);
2825 }
2826 TrailOp::FalseAssertionSet => {
2827 self.has_false_assertion = false;
2829 }
2830 TrailOp::NamedAssertionAdded { index } => {
2831 if self.named_assertions.len() > index {
2833 self.named_assertions.truncate(index);
2834 }
2835 }
2836 }
2837 }
2838 }
2839
2840 self.assertions.truncate(state.num_assertions);
2842 self.var_to_term.truncate(state.num_vars);
2843 self.has_false_assertion = state.has_false_assertion;
2844
2845 self.sat.pop();
2846 self.euf.pop();
2847 self.arith.pop();
2848 }
2849 }
2850
2851 pub fn reset(&mut self) {
2853 self.sat.reset();
2854 self.euf.reset();
2855 self.arith.reset();
2856 self.term_to_var.clear();
2857 self.var_to_term.clear();
2858 self.var_to_constraint.clear();
2859 self.var_to_parsed_arith.clear();
2860 self.assertions.clear();
2861 self.named_assertions.clear();
2862 self.model = None;
2863 self.unsat_core = None;
2864 self.context_stack.clear();
2865 self.trail.clear();
2866 self.logic = None;
2867 self.theory_processed_up_to = 0;
2868 self.has_false_assertion = false;
2869 }
2870
2871 #[must_use]
2873 pub fn config(&self) -> &SolverConfig {
2874 &self.config
2875 }
2876
2877 pub fn set_config(&mut self, config: SolverConfig) {
2879 self.config = config;
2880 }
2881
2882 #[must_use]
2884 pub fn stats(&self) -> &oxiz_sat::SolverStats {
2885 self.sat.stats()
2886 }
2887}
2888
2889#[cfg(test)]
2890mod tests {
2891 use super::*;
2892
2893 #[test]
2894 fn test_solver_empty() {
2895 let mut solver = Solver::new();
2896 let mut manager = TermManager::new();
2897 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
2898 }
2899
2900 #[test]
2901 fn test_solver_true() {
2902 let mut solver = Solver::new();
2903 let mut manager = TermManager::new();
2904 let t = manager.mk_true();
2905 solver.assert(t, &mut manager);
2906 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
2907 }
2908
2909 #[test]
2910 fn test_solver_false() {
2911 let mut solver = Solver::new();
2912 let mut manager = TermManager::new();
2913 let f = manager.mk_false();
2914 solver.assert(f, &mut manager);
2915 assert_eq!(solver.check(&mut manager), SolverResult::Unsat);
2916 }
2917
2918 #[test]
2919 fn test_solver_push_pop() {
2920 let mut solver = Solver::new();
2921 let mut manager = TermManager::new();
2922
2923 let t = manager.mk_true();
2924 solver.assert(t, &mut manager);
2925 solver.push();
2926
2927 let f = manager.mk_false();
2928 solver.assert(f, &mut manager);
2929 assert_eq!(solver.check(&mut manager), SolverResult::Unsat);
2930
2931 solver.pop();
2932 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
2933 }
2934
2935 #[test]
2936 fn test_unsat_core_trivial() {
2937 let mut solver = Solver::new();
2938 let mut manager = TermManager::new();
2939 solver.set_produce_unsat_cores(true);
2940
2941 let t = manager.mk_true();
2942 let f = manager.mk_false();
2943
2944 solver.assert_named(t, "a1", &mut manager);
2945 solver.assert_named(f, "a2", &mut manager);
2946 solver.assert_named(t, "a3", &mut manager);
2947
2948 assert_eq!(solver.check(&mut manager), SolverResult::Unsat);
2949
2950 let core = solver.get_unsat_core();
2951 assert!(core.is_some());
2952
2953 let core = core.unwrap();
2954 assert!(!core.is_empty());
2955 assert!(core.names.contains(&"a2".to_string()));
2956 }
2957
2958 #[test]
2959 fn test_unsat_core_not_produced_when_sat() {
2960 let mut solver = Solver::new();
2961 let mut manager = TermManager::new();
2962 solver.set_produce_unsat_cores(true);
2963
2964 let t = manager.mk_true();
2965 solver.assert_named(t, "a1", &mut manager);
2966 solver.assert_named(t, "a2", &mut manager);
2967
2968 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
2969 assert!(solver.get_unsat_core().is_none());
2970 }
2971
2972 #[test]
2973 fn test_unsat_core_disabled() {
2974 let mut solver = Solver::new();
2975 let mut manager = TermManager::new();
2976 let f = manager.mk_false();
2979 solver.assert(f, &mut manager);
2980 assert_eq!(solver.check(&mut manager), SolverResult::Unsat);
2981
2982 assert!(solver.get_unsat_core().is_none());
2984 }
2985
2986 #[test]
2987 fn test_boolean_encoding_and() {
2988 let mut solver = Solver::new();
2989 let mut manager = TermManager::new();
2990
2991 let p = manager.mk_var("p", manager.sorts.bool_sort);
2993 let q = manager.mk_var("q", manager.sorts.bool_sort);
2994 let and = manager.mk_and(vec![p, q]);
2995
2996 solver.assert(and, &mut manager);
2997 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
2998
2999 let model = solver.model().expect("Should have model");
3001 assert!(model.get(p).is_some());
3002 assert!(model.get(q).is_some());
3003 }
3004
3005 #[test]
3006 fn test_boolean_encoding_or() {
3007 let mut solver = Solver::new();
3008 let mut manager = TermManager::new();
3009
3010 let p = manager.mk_var("p", manager.sorts.bool_sort);
3012 let q = manager.mk_var("q", manager.sorts.bool_sort);
3013 let or = manager.mk_or(vec![p, q]);
3014 let not_p = manager.mk_not(p);
3015
3016 solver.assert(or, &mut manager);
3017 solver.assert(not_p, &mut manager);
3018 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
3019 }
3020
3021 #[test]
3022 fn test_boolean_encoding_implies() {
3023 let mut solver = Solver::new();
3024 let mut manager = TermManager::new();
3025
3026 let p = manager.mk_var("p", manager.sorts.bool_sort);
3028 let q = manager.mk_var("q", manager.sorts.bool_sort);
3029 let implies = manager.mk_implies(p, q);
3030 let not_q = manager.mk_not(q);
3031
3032 solver.assert(implies, &mut manager);
3033 solver.assert(p, &mut manager);
3034 solver.assert(not_q, &mut manager);
3035 assert_eq!(solver.check(&mut manager), SolverResult::Unsat);
3036 }
3037
3038 #[test]
3039 fn test_boolean_encoding_distinct() {
3040 let mut solver = Solver::new();
3041 let mut manager = TermManager::new();
3042
3043 let p = manager.mk_var("p", manager.sorts.bool_sort);
3045 let q = manager.mk_var("q", manager.sorts.bool_sort);
3046 let r = manager.mk_var("r", manager.sorts.bool_sort);
3047 let distinct = manager.mk_distinct(vec![p, q, r]);
3048
3049 solver.assert(distinct, &mut manager);
3050 solver.assert(p, &mut manager);
3051 solver.assert(q, &mut manager);
3052 assert_eq!(solver.check(&mut manager), SolverResult::Unsat);
3053 }
3054
3055 #[test]
3056 fn test_model_evaluation_bool() {
3057 let mut solver = Solver::new();
3058 let mut manager = TermManager::new();
3059
3060 let p = manager.mk_var("p", manager.sorts.bool_sort);
3061 let q = manager.mk_var("q", manager.sorts.bool_sort);
3062
3063 solver.assert(p, &mut manager);
3065 solver.assert(manager.mk_not(q), &mut manager);
3066 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
3067
3068 let model = solver.model().expect("Should have model");
3069
3070 let p_val = model.eval(p, &mut manager);
3072 assert_eq!(p_val, manager.mk_true());
3073
3074 let q_val = model.eval(q, &mut manager);
3076 assert_eq!(q_val, manager.mk_false());
3077
3078 let and_term = manager.mk_and(vec![p, q]);
3080 let and_val = model.eval(and_term, &mut manager);
3081 assert_eq!(and_val, manager.mk_false());
3082
3083 let or_term = manager.mk_or(vec![p, q]);
3085 let or_val = model.eval(or_term, &mut manager);
3086 assert_eq!(or_val, manager.mk_true());
3087 }
3088
3089 #[test]
3090 fn test_model_evaluation_ite() {
3091 let mut solver = Solver::new();
3092 let mut manager = TermManager::new();
3093
3094 let p = manager.mk_var("p", manager.sorts.bool_sort);
3095 let q = manager.mk_var("q", manager.sorts.bool_sort);
3096 let r = manager.mk_var("r", manager.sorts.bool_sort);
3097
3098 solver.assert(p, &mut manager);
3100 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
3101
3102 let model = solver.model().expect("Should have model");
3103
3104 let ite_term = manager.mk_ite(p, q, r);
3106 let ite_val = model.eval(ite_term, &mut manager);
3107 let q_val = model.eval(q, &mut manager);
3109 assert_eq!(ite_val, q_val);
3110 }
3111
3112 #[test]
3113 fn test_model_evaluation_implies() {
3114 let mut solver = Solver::new();
3115 let mut manager = TermManager::new();
3116
3117 let p = manager.mk_var("p", manager.sorts.bool_sort);
3118 let q = manager.mk_var("q", manager.sorts.bool_sort);
3119
3120 solver.assert(manager.mk_not(p), &mut manager);
3122 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
3123
3124 let model = solver.model().expect("Should have model");
3125
3126 let implies_term = manager.mk_implies(p, q);
3128 let implies_val = model.eval(implies_term, &mut manager);
3129 assert_eq!(implies_val, manager.mk_true());
3130 }
3131
3132 #[test]
3133 fn test_arithmetic_model_generation() {
3134 use num_bigint::BigInt;
3135
3136 let mut solver = Solver::new();
3137 let mut manager = TermManager::new();
3138
3139 let x = manager.mk_var("x", manager.sorts.int_sort);
3141 let y = manager.mk_var("y", manager.sorts.int_sort);
3142
3143 let ten = manager.mk_int(BigInt::from(10));
3145 let zero = manager.mk_int(BigInt::from(0));
3146 let sum = manager.mk_add(vec![x, y]);
3147
3148 let eq = manager.mk_eq(sum, ten);
3149 let x_ge_0 = manager.mk_ge(x, zero);
3150 let y_ge_0 = manager.mk_ge(y, zero);
3151
3152 solver.assert(eq, &mut manager);
3153 solver.assert(x_ge_0, &mut manager);
3154 solver.assert(y_ge_0, &mut manager);
3155
3156 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
3157
3158 let model = solver.model();
3160 assert!(model.is_some(), "Should have a model for SAT result");
3161 }
3162
3163 #[test]
3164 fn test_model_pretty_print() {
3165 let mut solver = Solver::new();
3166 let mut manager = TermManager::new();
3167
3168 let p = manager.mk_var("p", manager.sorts.bool_sort);
3169 let q = manager.mk_var("q", manager.sorts.bool_sort);
3170
3171 solver.assert(p, &mut manager);
3172 solver.assert(manager.mk_not(q), &mut manager);
3173 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
3174
3175 let model = solver.model().expect("Should have model");
3176 let pretty = model.pretty_print(&manager);
3177
3178 assert!(pretty.contains("(model"));
3180 assert!(pretty.contains("define-fun"));
3181 assert!(pretty.contains("p") || pretty.contains("q"));
3183 }
3184
3185 #[test]
3186 fn test_trail_based_undo_assertions() {
3187 let mut solver = Solver::new();
3188 let mut manager = TermManager::new();
3189
3190 let p = manager.mk_var("p", manager.sorts.bool_sort);
3191 let q = manager.mk_var("q", manager.sorts.bool_sort);
3192
3193 assert_eq!(solver.assertions.len(), 0);
3195 assert_eq!(solver.trail.len(), 0);
3196
3197 solver.assert(p, &mut manager);
3199 assert_eq!(solver.assertions.len(), 1);
3200 assert!(!solver.trail.is_empty());
3201
3202 solver.push();
3204 let trail_len_after_push = solver.trail.len();
3205 solver.assert(q, &mut manager);
3206 assert_eq!(solver.assertions.len(), 2);
3207 assert!(solver.trail.len() > trail_len_after_push);
3208
3209 solver.pop();
3211 assert_eq!(solver.assertions.len(), 1);
3212 assert_eq!(solver.assertions[0], p);
3213 }
3214
3215 #[test]
3216 fn test_trail_based_undo_variables() {
3217 let mut solver = Solver::new();
3218 let mut manager = TermManager::new();
3219
3220 let p = manager.mk_var("p", manager.sorts.bool_sort);
3221 let q = manager.mk_var("q", manager.sorts.bool_sort);
3222
3223 solver.assert(p, &mut manager);
3225 let initial_var_count = solver.term_to_var.len();
3226
3227 solver.push();
3229 solver.assert(q, &mut manager);
3230 assert!(solver.term_to_var.len() >= initial_var_count);
3231
3232 solver.pop();
3234 assert_eq!(solver.assertions.len(), 1);
3236 }
3237
3238 #[test]
3239 fn test_trail_based_undo_constraints() {
3240 use num_bigint::BigInt;
3241
3242 let mut solver = Solver::new();
3243 let mut manager = TermManager::new();
3244
3245 let x = manager.mk_var("x", manager.sorts.int_sort);
3246 let zero = manager.mk_int(BigInt::from(0));
3247
3248 let c1 = manager.mk_ge(x, zero);
3250 solver.assert(c1, &mut manager);
3251 let initial_constraint_count = solver.var_to_constraint.len();
3252
3253 solver.push();
3255 let ten = manager.mk_int(BigInt::from(10));
3256 let c2 = manager.mk_le(x, ten);
3257 solver.assert(c2, &mut manager);
3258 assert!(solver.var_to_constraint.len() >= initial_constraint_count);
3259
3260 solver.pop();
3262 assert_eq!(solver.var_to_constraint.len(), initial_constraint_count);
3263 }
3264
3265 #[test]
3266 fn test_trail_based_undo_false_assertion() {
3267 let mut solver = Solver::new();
3268 let mut manager = TermManager::new();
3269
3270 assert!(!solver.has_false_assertion);
3271
3272 solver.push();
3273 solver.assert(manager.mk_false(), &mut manager);
3274 assert!(solver.has_false_assertion);
3275
3276 solver.pop();
3277 assert!(!solver.has_false_assertion);
3278 }
3279
3280 #[test]
3281 fn test_trail_based_undo_named_assertions() {
3282 let mut solver = Solver::new();
3283 let mut manager = TermManager::new();
3284 solver.set_produce_unsat_cores(true);
3285
3286 let p = manager.mk_var("p", manager.sorts.bool_sort);
3287
3288 solver.assert_named(p, "assertion1", &mut manager);
3289 assert_eq!(solver.named_assertions.len(), 1);
3290
3291 solver.push();
3292 let q = manager.mk_var("q", manager.sorts.bool_sort);
3293 solver.assert_named(q, "assertion2", &mut manager);
3294 assert_eq!(solver.named_assertions.len(), 2);
3295
3296 solver.pop();
3297 assert_eq!(solver.named_assertions.len(), 1);
3298 assert_eq!(
3299 solver.named_assertions[0].name,
3300 Some("assertion1".to_string())
3301 );
3302 }
3303
3304 #[test]
3305 fn test_trail_based_undo_nested_push_pop() {
3306 let mut solver = Solver::new();
3307 let mut manager = TermManager::new();
3308
3309 let p = manager.mk_var("p", manager.sorts.bool_sort);
3310 solver.assert(p, &mut manager);
3311
3312 solver.push();
3314 let q = manager.mk_var("q", manager.sorts.bool_sort);
3315 solver.assert(q, &mut manager);
3316 assert_eq!(solver.assertions.len(), 2);
3317
3318 solver.push();
3320 let r = manager.mk_var("r", manager.sorts.bool_sort);
3321 solver.assert(r, &mut manager);
3322 assert_eq!(solver.assertions.len(), 3);
3323
3324 solver.pop();
3326 assert_eq!(solver.assertions.len(), 2);
3327
3328 solver.pop();
3330 assert_eq!(solver.assertions.len(), 1);
3331 assert_eq!(solver.assertions[0], p);
3332 }
3333
3334 #[test]
3335 fn test_config_presets() {
3336 let _fast = SolverConfig::fast();
3338 let _balanced = SolverConfig::balanced();
3339 let _thorough = SolverConfig::thorough();
3340 let _minimal = SolverConfig::minimal();
3341 }
3342
3343 #[test]
3344 fn test_config_fast_characteristics() {
3345 let config = SolverConfig::fast();
3346
3347 assert!(!config.enable_variable_elimination);
3349 assert!(!config.enable_blocked_clause_elimination);
3350 assert!(!config.enable_symmetry_breaking);
3351 assert!(!config.enable_inprocessing);
3352 assert!(!config.enable_clause_subsumption);
3353
3354 assert!(config.enable_clause_minimization);
3356 assert!(config.simplify);
3357
3358 assert_eq!(config.restart_strategy, RestartStrategy::Geometric);
3360 }
3361
3362 #[test]
3363 fn test_config_balanced_characteristics() {
3364 let config = SolverConfig::balanced();
3365
3366 assert!(config.enable_variable_elimination);
3368 assert!(config.enable_blocked_clause_elimination);
3369 assert!(config.enable_inprocessing);
3370 assert!(config.enable_clause_minimization);
3371 assert!(config.enable_clause_subsumption);
3372 assert!(config.simplify);
3373
3374 assert!(!config.enable_symmetry_breaking);
3376
3377 assert_eq!(config.restart_strategy, RestartStrategy::Glucose);
3379
3380 assert_eq!(config.variable_elimination_limit, 1000);
3382 assert_eq!(config.inprocessing_interval, 10000);
3383 }
3384
3385 #[test]
3386 fn test_config_thorough_characteristics() {
3387 let config = SolverConfig::thorough();
3388
3389 assert!(config.enable_variable_elimination);
3391 assert!(config.enable_blocked_clause_elimination);
3392 assert!(config.enable_symmetry_breaking); assert!(config.enable_inprocessing);
3394 assert!(config.enable_clause_minimization);
3395 assert!(config.enable_clause_subsumption);
3396 assert!(config.simplify);
3397
3398 assert_eq!(config.variable_elimination_limit, 5000);
3400 assert_eq!(config.inprocessing_interval, 5000);
3401 }
3402
3403 #[test]
3404 fn test_config_minimal_characteristics() {
3405 let config = SolverConfig::minimal();
3406
3407 assert!(!config.simplify);
3409 assert!(!config.enable_variable_elimination);
3410 assert!(!config.enable_blocked_clause_elimination);
3411 assert!(!config.enable_symmetry_breaking);
3412 assert!(!config.enable_inprocessing);
3413 assert!(!config.enable_clause_minimization);
3414 assert!(!config.enable_clause_subsumption);
3415
3416 assert_eq!(config.theory_mode, TheoryMode::Lazy);
3418
3419 assert_eq!(config.num_threads, 1);
3421 }
3422
3423 #[test]
3424 fn test_config_builder_pattern() {
3425 let config = SolverConfig::fast()
3427 .with_proof()
3428 .with_timeout(5000)
3429 .with_max_conflicts(1000)
3430 .with_max_decisions(2000)
3431 .with_parallel(8)
3432 .with_restart_strategy(RestartStrategy::Luby)
3433 .with_theory_mode(TheoryMode::Lazy);
3434
3435 assert!(config.proof);
3436 assert_eq!(config.timeout_ms, 5000);
3437 assert_eq!(config.max_conflicts, 1000);
3438 assert_eq!(config.max_decisions, 2000);
3439 assert!(config.parallel);
3440 assert_eq!(config.num_threads, 8);
3441 assert_eq!(config.restart_strategy, RestartStrategy::Luby);
3442 assert_eq!(config.theory_mode, TheoryMode::Lazy);
3443 }
3444
3445 #[test]
3446 fn test_solver_with_different_configs() {
3447 let mut manager = TermManager::new();
3448
3449 let mut solver_fast = Solver::with_config(SolverConfig::fast());
3451 let mut solver_balanced = Solver::with_config(SolverConfig::balanced());
3452 let mut solver_thorough = Solver::with_config(SolverConfig::thorough());
3453 let mut solver_minimal = Solver::with_config(SolverConfig::minimal());
3454
3455 let t = manager.mk_true();
3457 solver_fast.assert(t, &mut manager);
3458 solver_balanced.assert(t, &mut manager);
3459 solver_thorough.assert(t, &mut manager);
3460 solver_minimal.assert(t, &mut manager);
3461
3462 assert_eq!(solver_fast.check(&mut manager), SolverResult::Sat);
3463 assert_eq!(solver_balanced.check(&mut manager), SolverResult::Sat);
3464 assert_eq!(solver_thorough.check(&mut manager), SolverResult::Sat);
3465 assert_eq!(solver_minimal.check(&mut manager), SolverResult::Sat);
3466 }
3467
3468 #[test]
3469 fn test_config_default_is_balanced() {
3470 let default = SolverConfig::default();
3471 let balanced = SolverConfig::balanced();
3472
3473 assert_eq!(
3475 default.enable_variable_elimination,
3476 balanced.enable_variable_elimination
3477 );
3478 assert_eq!(
3479 default.enable_clause_minimization,
3480 balanced.enable_clause_minimization
3481 );
3482 assert_eq!(
3483 default.enable_symmetry_breaking,
3484 balanced.enable_symmetry_breaking
3485 );
3486 assert_eq!(default.restart_strategy, balanced.restart_strategy);
3487 }
3488
3489 #[test]
3490 fn test_theory_combination_arith_solver() {
3491 use oxiz_theories::arithmetic::ArithSolver;
3492 use oxiz_theories::{EqualityNotification, TheoryCombination};
3493
3494 let mut arith = ArithSolver::lra();
3495 let mut manager = TermManager::new();
3496
3497 let x = manager.mk_var("x", manager.sorts.int_sort);
3499 let y = manager.mk_var("y", manager.sorts.int_sort);
3500
3501 let _x_var = arith.intern(x);
3503 let _y_var = arith.intern(y);
3504
3505 let eq_notification = EqualityNotification {
3507 lhs: x,
3508 rhs: y,
3509 reason: None,
3510 };
3511
3512 let accepted = arith.notify_equality(eq_notification);
3513 assert!(
3514 accepted,
3515 "ArithSolver should accept equality notification for known terms"
3516 );
3517
3518 assert!(
3520 arith.is_relevant(x),
3521 "x should be relevant to arithmetic solver"
3522 );
3523 assert!(
3524 arith.is_relevant(y),
3525 "y should be relevant to arithmetic solver"
3526 );
3527
3528 let z = manager.mk_var("z", manager.sorts.int_sort);
3530 assert!(
3531 !arith.is_relevant(z),
3532 "z should not be relevant (not interned)"
3533 );
3534
3535 let eq_unknown = EqualityNotification {
3537 lhs: x,
3538 rhs: z,
3539 reason: None,
3540 };
3541 let accepted_unknown = arith.notify_equality(eq_unknown);
3542 assert!(
3543 !accepted_unknown,
3544 "ArithSolver should reject equality with unknown term"
3545 );
3546 }
3547
3548 #[test]
3549 fn test_theory_combination_get_shared_equalities() {
3550 use oxiz_theories::TheoryCombination;
3551 use oxiz_theories::arithmetic::ArithSolver;
3552
3553 let arith = ArithSolver::lra();
3554
3555 let shared = arith.get_shared_equalities();
3557 assert!(
3558 shared.is_empty(),
3559 "ArithSolver should return empty shared equalities (placeholder)"
3560 );
3561 }
3562
3563 #[test]
3564 fn test_equality_notification_fields() {
3565 use oxiz_theories::EqualityNotification;
3566
3567 let mut manager = TermManager::new();
3568 let x = manager.mk_var("x", manager.sorts.int_sort);
3569 let y = manager.mk_var("y", manager.sorts.int_sort);
3570
3571 let eq1 = EqualityNotification {
3573 lhs: x,
3574 rhs: y,
3575 reason: Some(x),
3576 };
3577 assert_eq!(eq1.lhs, x);
3578 assert_eq!(eq1.rhs, y);
3579 assert_eq!(eq1.reason, Some(x));
3580
3581 let eq2 = EqualityNotification {
3583 lhs: x,
3584 rhs: y,
3585 reason: None,
3586 };
3587 assert_eq!(eq2.reason, None);
3588
3589 let eq3 = eq1;
3591 assert_eq!(eq3.lhs, eq1.lhs);
3592 assert_eq!(eq3.rhs, eq1.rhs);
3593 }
3594
3595 #[test]
3596 fn test_assert_many() {
3597 let mut solver = Solver::new();
3598 let mut manager = TermManager::new();
3599
3600 let p = manager.mk_var("p", manager.sorts.bool_sort);
3601 let q = manager.mk_var("q", manager.sorts.bool_sort);
3602 let r = manager.mk_var("r", manager.sorts.bool_sort);
3603
3604 solver.assert_many(&[p, q, r], &mut manager);
3606
3607 assert_eq!(solver.num_assertions(), 3);
3608 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
3609 }
3610
3611 #[test]
3612 fn test_num_assertions_and_variables() {
3613 let mut solver = Solver::new();
3614 let mut manager = TermManager::new();
3615
3616 assert_eq!(solver.num_assertions(), 0);
3617 assert!(!solver.has_assertions());
3618
3619 let p = manager.mk_var("p", manager.sorts.bool_sort);
3620 let q = manager.mk_var("q", manager.sorts.bool_sort);
3621
3622 solver.assert(p, &mut manager);
3623 assert_eq!(solver.num_assertions(), 1);
3624 assert!(solver.has_assertions());
3625
3626 solver.assert(q, &mut manager);
3627 assert_eq!(solver.num_assertions(), 2);
3628
3629 assert!(solver.num_variables() > 0);
3631 }
3632
3633 #[test]
3634 fn test_context_level() {
3635 let mut solver = Solver::new();
3636
3637 assert_eq!(solver.context_level(), 0);
3638
3639 solver.push();
3640 assert_eq!(solver.context_level(), 1);
3641
3642 solver.push();
3643 assert_eq!(solver.context_level(), 2);
3644
3645 solver.pop();
3646 assert_eq!(solver.context_level(), 1);
3647
3648 solver.pop();
3649 assert_eq!(solver.context_level(), 0);
3650 }
3651
3652 #[test]
3655 fn test_quantifier_basic_forall() {
3656 let mut solver = Solver::new();
3657 let mut manager = TermManager::new();
3658 let bool_sort = manager.sorts.bool_sort;
3659
3660 let x = manager.mk_var("x", bool_sort);
3663 let p_x = manager.mk_apply("P", [x], bool_sort);
3664 let forall = manager.mk_forall([("x", bool_sort)], p_x);
3665
3666 solver.assert(forall, &mut manager);
3667
3668 let result = solver.check(&mut manager);
3670 assert!(
3672 result == SolverResult::Sat || result == SolverResult::Unknown,
3673 "Basic forall should not be unsat"
3674 );
3675 }
3676
3677 #[test]
3678 fn test_quantifier_basic_exists() {
3679 let mut solver = Solver::new();
3680 let mut manager = TermManager::new();
3681 let bool_sort = manager.sorts.bool_sort;
3682
3683 let x = manager.mk_var("x", bool_sort);
3685 let p_x = manager.mk_apply("P", [x], bool_sort);
3686 let exists = manager.mk_exists([("x", bool_sort)], p_x);
3687
3688 solver.assert(exists, &mut manager);
3689
3690 let result = solver.check(&mut manager);
3691 assert!(
3692 result == SolverResult::Sat || result == SolverResult::Unknown,
3693 "Basic exists should not be unsat"
3694 );
3695 }
3696
3697 #[test]
3698 fn test_quantifier_with_ground_terms() {
3699 let mut solver = Solver::new();
3700 let mut manager = TermManager::new();
3701 let int_sort = manager.sorts.int_sort;
3702 let bool_sort = manager.sorts.bool_sort;
3703
3704 let zero = manager.mk_int(0);
3706 let one = manager.mk_int(1);
3707
3708 let p_0 = manager.mk_apply("P", [zero], bool_sort);
3710 let p_1 = manager.mk_apply("P", [one], bool_sort);
3711 solver.assert(p_0, &mut manager);
3712 solver.assert(p_1, &mut manager);
3713
3714 let x = manager.mk_var("x", int_sort);
3716 let p_x = manager.mk_apply("P", [x], bool_sort);
3717 let forall = manager.mk_forall([("x", int_sort)], p_x);
3718 solver.assert(forall, &mut manager);
3719
3720 let result = solver.check(&mut manager);
3721 assert!(
3723 result == SolverResult::Sat || result == SolverResult::Unknown,
3724 "Quantifier with matching ground terms should be satisfiable"
3725 );
3726 }
3727
3728 #[test]
3729 fn test_quantifier_instantiation() {
3730 let mut solver = Solver::new();
3731 let mut manager = TermManager::new();
3732 let int_sort = manager.sorts.int_sort;
3733 let bool_sort = manager.sorts.bool_sort;
3734
3735 let c = manager.mk_apply("c", [], int_sort);
3737
3738 let x = manager.mk_var("x", int_sort);
3740 let f_x = manager.mk_apply("f", [x], int_sort);
3741 let zero = manager.mk_int(0);
3742 let f_x_gt_0 = manager.mk_gt(f_x, zero);
3743 let forall = manager.mk_forall([("x", int_sort)], f_x_gt_0);
3744 solver.assert(forall, &mut manager);
3745
3746 let f_c = manager.mk_apply("f", [c], int_sort);
3748 let f_c_exists = manager.mk_apply("exists_f_c", [f_c], bool_sort);
3749 solver.assert(f_c_exists, &mut manager);
3750
3751 let result = solver.check(&mut manager);
3752 assert!(
3754 result == SolverResult::Sat || result == SolverResult::Unknown,
3755 "Quantifier instantiation test"
3756 );
3757 }
3758
3759 #[test]
3760 fn test_quantifier_mbqi_solver_integration() {
3761 use crate::mbqi::MBQISolver;
3762
3763 let mut mbqi = MBQISolver::new();
3764 let mut manager = TermManager::new();
3765 let int_sort = manager.sorts.int_sort;
3766
3767 let x = manager.mk_var("x", int_sort);
3769 let zero = manager.mk_int(0);
3770 let x_gt_0 = manager.mk_gt(x, zero);
3771 let forall = manager.mk_forall([("x", int_sort)], x_gt_0);
3772
3773 mbqi.add_quantifier(forall, &manager);
3775
3776 let one = manager.mk_int(1);
3778 let two = manager.mk_int(2);
3779 mbqi.add_candidate(one, int_sort);
3780 mbqi.add_candidate(two, int_sort);
3781
3782 assert!(mbqi.is_enabled(), "MBQI should be enabled by default");
3784 }
3785
3786 #[test]
3787 fn test_quantifier_pattern_matching() {
3788 let mut solver = Solver::new();
3789 let mut manager = TermManager::new();
3790 let int_sort = manager.sorts.int_sort;
3791
3792 let x = manager.mk_var("x", int_sort);
3794 let f_x = manager.mk_apply("f", [x], int_sort);
3795 let g_x = manager.mk_apply("g", [x], int_sort);
3796 let body = manager.mk_eq(f_x, g_x);
3797
3798 let pattern: smallvec::SmallVec<[_; 2]> = smallvec::smallvec![f_x];
3800 let patterns: smallvec::SmallVec<[_; 2]> = smallvec::smallvec![pattern];
3801
3802 let forall = manager.mk_forall_with_patterns([("x", int_sort)], body, patterns);
3803 solver.assert(forall, &mut manager);
3804
3805 let c = manager.mk_apply("c", [], int_sort);
3807 let f_c = manager.mk_apply("f", [c], int_sort);
3808 let f_c_eq_c = manager.mk_eq(f_c, c);
3809 solver.assert(f_c_eq_c, &mut manager);
3810
3811 let result = solver.check(&mut manager);
3812 assert!(
3813 result == SolverResult::Sat || result == SolverResult::Unknown,
3814 "Pattern matching should allow instantiation"
3815 );
3816 }
3817
3818 #[test]
3819 fn test_quantifier_multiple() {
3820 let mut solver = Solver::new();
3821 let mut manager = TermManager::new();
3822 let int_sort = manager.sorts.int_sort;
3823
3824 let x = manager.mk_var("x", int_sort);
3826 let y = manager.mk_var("y", int_sort);
3827 let x_plus_y = manager.mk_add([x, y]);
3828 let y_plus_x = manager.mk_add([y, x]);
3829 let commutative = manager.mk_eq(x_plus_y, y_plus_x);
3830
3831 let inner_forall = manager.mk_forall([("y", int_sort)], commutative);
3832 let outer_forall = manager.mk_forall([("x", int_sort)], inner_forall);
3833
3834 solver.assert(outer_forall, &mut manager);
3835
3836 let result = solver.check(&mut manager);
3837 assert!(
3838 result == SolverResult::Sat || result == SolverResult::Unknown,
3839 "Nested forall should be handled"
3840 );
3841 }
3842
3843 #[test]
3844 fn test_quantifier_with_model() {
3845 let mut solver = Solver::new();
3846 let mut manager = TermManager::new();
3847 let bool_sort = manager.sorts.bool_sort;
3848
3849 let p = manager.mk_var("p", bool_sort);
3851 solver.assert(p, &mut manager);
3852
3853 let x = manager.mk_var("x", bool_sort);
3855 let not_x = manager.mk_not(x);
3856 let x_or_not_x = manager.mk_or([x, not_x]);
3857 let tautology = manager.mk_forall([("x", bool_sort)], x_or_not_x);
3858 solver.assert(tautology, &mut manager);
3859
3860 let result = solver.check(&mut manager);
3861 assert_eq!(
3862 result,
3863 SolverResult::Sat,
3864 "Tautology in quantifier should be SAT"
3865 );
3866
3867 if let Some(model) = solver.model() {
3869 assert!(model.size() > 0, "Model should have assignments");
3870 }
3871 }
3872
3873 #[test]
3874 fn test_quantifier_push_pop() {
3875 let mut solver = Solver::new();
3876 let mut manager = TermManager::new();
3877 let int_sort = manager.sorts.int_sort;
3878
3879 let x = manager.mk_var("x", int_sort);
3881 let zero = manager.mk_int(0);
3882 let x_gt_0 = manager.mk_gt(x, zero);
3883 let forall = manager.mk_forall([("x", int_sort)], x_gt_0);
3884
3885 solver.push();
3886 solver.assert(forall, &mut manager);
3887
3888 let result1 = solver.check(&mut manager);
3889 assert!(
3892 result1 == SolverResult::Unsat || result1 == SolverResult::Unknown,
3893 "forall x. x > 0 should be Unsat or Unknown, got {:?}",
3894 result1
3895 );
3896
3897 solver.pop();
3898
3899 let result2 = solver.check(&mut manager);
3901 assert_eq!(
3902 result2,
3903 SolverResult::Sat,
3904 "After pop, should be trivially SAT"
3905 );
3906 }
3907
3908 #[test]
3915 #[ignore = "Requires complete arithmetic constraint encoding to simplex (see process_constraint in solver.rs)"]
3916 fn test_integer_contradiction_unsat() {
3917 use num_bigint::BigInt;
3918
3919 let mut solver = Solver::new();
3920 let mut manager = TermManager::new();
3921
3922 let x = manager.mk_var("x", manager.sorts.int_sort);
3924 let zero = manager.mk_int(BigInt::from(0));
3925
3926 let x_ge_0 = manager.mk_ge(x, zero);
3928 solver.assert(x_ge_0, &mut manager);
3929
3930 let x_lt_0 = manager.mk_lt(x, zero);
3932 solver.assert(x_lt_0, &mut manager);
3933
3934 let result = solver.check(&mut manager);
3936 assert_eq!(
3937 result,
3938 SolverResult::Unsat,
3939 "x >= 0 AND x < 0 should be UNSAT"
3940 );
3941 }
3942}