1use crate::mbqi::{MBQIIntegration, MBQIResult};
4use crate::simplify::Simplifier;
5use num_rational::Rational64;
6use num_traits::{One, ToPrimitive, Zero};
7use oxiz_core::ast::{RoundingMode, 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 return manager.mk_true();
645 }
646
647 if let Some(lhs_term) = manager.get(lhs_val)
653 && lhs_term.sort == manager.sorts.bool_sort
654 {
655 if let Some(rhs_term) = manager.get(rhs_val) {
657 match rhs_term.kind {
658 TermKind::True => return lhs_val,
659 TermKind::False => return manager.mk_not(lhs_val),
660 _ => {}
661 }
662 }
663 match lhs_term.kind {
665 TermKind::True => return rhs_val,
666 TermKind::False => return manager.mk_not(rhs_val),
667 _ => {}
668 }
669 }
670
671 manager.mk_eq(lhs_val, rhs_val)
672 }
673
674 TermKind::Neg(arg) => {
676 let arg_val = self.eval(arg, manager);
677 if let Some(t) = manager.get(arg_val) {
678 match &t.kind {
679 TermKind::IntConst(n) => return manager.mk_int(-n),
680 TermKind::RealConst(r) => return manager.mk_real(-r),
681 _ => {}
682 }
683 }
684 manager.mk_not(arg_val)
685 }
686
687 TermKind::Add(ref args) => {
688 let eval_args: Vec<_> = args.iter().map(|&a| self.eval(a, manager)).collect();
689 manager.mk_add(eval_args)
690 }
691
692 TermKind::Sub(lhs, rhs) => {
693 let lhs_val = self.eval(lhs, manager);
694 let rhs_val = self.eval(rhs, manager);
695 manager.mk_sub(lhs_val, rhs_val)
696 }
697
698 TermKind::Mul(ref args) => {
699 let eval_args: Vec<_> = args.iter().map(|&a| self.eval(a, manager)).collect();
700 manager.mk_mul(eval_args)
701 }
702
703 _ => self.get(term).unwrap_or(term),
705 }
706 }
707}
708
709impl Default for Model {
710 fn default() -> Self {
711 Self::new()
712 }
713}
714
715impl Model {
716 pub fn pretty_print(&self, manager: &TermManager) -> String {
718 if self.assignments.is_empty() {
719 return "(model)".to_string();
720 }
721
722 let mut lines = vec!["(model".to_string()];
723 let printer = oxiz_core::smtlib::Printer::new(manager);
724
725 for (&var, &value) in &self.assignments {
726 if let Some(term) = manager.get(var) {
727 if let TermKind::Var(name) = &term.kind {
729 let sort_str = Self::format_sort(term.sort, manager);
730 let value_str = printer.print_term(value);
731 let name_str = format!("{:?}", name);
733 lines.push(format!(
734 " (define-fun {} () {} {})",
735 name_str, sort_str, value_str
736 ));
737 }
738 }
739 }
740 lines.push(")".to_string());
741 lines.join("\n")
742 }
743
744 fn format_sort(sort: oxiz_core::sort::SortId, manager: &TermManager) -> String {
746 if sort == manager.sorts.bool_sort {
747 "Bool".to_string()
748 } else if sort == manager.sorts.int_sort {
749 "Int".to_string()
750 } else if sort == manager.sorts.real_sort {
751 "Real".to_string()
752 } else if let Some(s) = manager.sorts.get(sort) {
753 if let Some(w) = s.bitvec_width() {
754 format!("(_ BitVec {})", w)
755 } else {
756 "Unknown".to_string()
757 }
758 } else {
759 "Unknown".to_string()
760 }
761 }
762}
763
764#[derive(Debug, Clone)]
766pub struct NamedAssertion {
767 #[allow(dead_code)]
769 pub term: TermId,
770 pub name: Option<String>,
772 pub index: u32,
774}
775
776#[derive(Debug, Clone)]
778pub struct UnsatCore {
779 pub names: Vec<String>,
781 pub indices: Vec<u32>,
783}
784
785impl UnsatCore {
786 #[must_use]
788 pub fn new() -> Self {
789 Self {
790 names: Vec::new(),
791 indices: Vec::new(),
792 }
793 }
794
795 #[must_use]
797 pub fn is_empty(&self) -> bool {
798 self.indices.is_empty()
799 }
800
801 #[must_use]
803 pub fn len(&self) -> usize {
804 self.indices.len()
805 }
806}
807
808impl Default for UnsatCore {
809 fn default() -> Self {
810 Self::new()
811 }
812}
813
814#[derive(Debug)]
816pub struct Solver {
817 config: SolverConfig,
819 sat: SatSolver,
821 euf: EufSolver,
823 arith: ArithSolver,
825 bv: BvSolver,
827 nlsat: Option<oxiz_theories::nlsat::NlsatTheory>,
829 mbqi: MBQIIntegration,
831 has_quantifiers: bool,
833 term_to_var: FxHashMap<TermId, Var>,
835 var_to_term: Vec<TermId>,
837 var_to_constraint: FxHashMap<Var, Constraint>,
839 var_to_parsed_arith: FxHashMap<Var, ParsedArithConstraint>,
841 logic: Option<String>,
843 assertions: Vec<TermId>,
845 named_assertions: Vec<NamedAssertion>,
847 #[allow(dead_code)]
850 assumption_vars: FxHashMap<u32, Var>,
851 model: Option<Model>,
853 unsat_core: Option<UnsatCore>,
855 context_stack: Vec<ContextState>,
857 trail: Vec<TrailOp>,
859 theory_processed_up_to: usize,
861 produce_unsat_cores: bool,
863 has_false_assertion: bool,
865 polarities: FxHashMap<TermId, Polarity>,
867 polarity_aware: bool,
869 theory_aware_branching: bool,
871 proof: Option<Proof>,
873 simplifier: Simplifier,
875 statistics: Statistics,
877 bv_terms: FxHashSet<TermId>,
879 has_bv_arith_ops: bool,
882 arith_terms: FxHashSet<TermId>,
884 dt_var_constructors: FxHashMap<TermId, lasso::Spur>,
887}
888
889#[derive(Debug, Clone, Copy)]
891#[allow(dead_code)]
892pub struct TheoryDecision {
893 pub var: Var,
895 pub value: bool,
897 pub priority: i32,
899}
900
901struct TheoryManager<'a> {
903 manager: &'a TermManager,
905 euf: &'a mut EufSolver,
907 arith: &'a mut ArithSolver,
909 bv: &'a mut BvSolver,
911 bv_terms: &'a FxHashSet<TermId>,
913 var_to_constraint: &'a FxHashMap<Var, Constraint>,
915 var_to_parsed_arith: &'a FxHashMap<Var, ParsedArithConstraint>,
917 term_to_var: &'a FxHashMap<TermId, Var>,
919 level_stack: Vec<usize>,
921 processed_count: usize,
923 theory_mode: TheoryMode,
925 pending_assignments: Vec<(Lit, bool)>,
927 #[allow(dead_code)]
929 decision_hints: Vec<TheoryDecision>,
930 pending_equalities: Vec<EqualityNotification>,
932 processed_equalities: FxHashMap<(TermId, TermId), bool>,
934 statistics: &'a mut Statistics,
936 max_conflicts: u64,
938 #[allow(dead_code)]
940 max_decisions: u64,
941 has_bv_arith_ops: bool,
943}
944
945impl<'a> TheoryManager<'a> {
946 #[allow(clippy::too_many_arguments)]
947 fn new(
948 manager: &'a TermManager,
949 euf: &'a mut EufSolver,
950 arith: &'a mut ArithSolver,
951 bv: &'a mut BvSolver,
952 bv_terms: &'a FxHashSet<TermId>,
953 var_to_constraint: &'a FxHashMap<Var, Constraint>,
954 var_to_parsed_arith: &'a FxHashMap<Var, ParsedArithConstraint>,
955 term_to_var: &'a FxHashMap<TermId, Var>,
956 theory_mode: TheoryMode,
957 statistics: &'a mut Statistics,
958 max_conflicts: u64,
959 max_decisions: u64,
960 has_bv_arith_ops: bool,
961 ) -> Self {
962 Self {
963 manager,
964 euf,
965 arith,
966 bv,
967 bv_terms,
968 var_to_constraint,
969 var_to_parsed_arith,
970 term_to_var,
971 level_stack: vec![0],
972 processed_count: 0,
973 theory_mode,
974 pending_assignments: Vec::new(),
975 decision_hints: Vec::new(),
976 pending_equalities: Vec::new(),
977 processed_equalities: FxHashMap::default(),
978 statistics,
979 max_conflicts,
980 max_decisions,
981 has_bv_arith_ops,
982 }
983 }
984
985 #[allow(dead_code)]
988 fn propagate_equalities(&mut self) -> TheoryCheckResult {
989 while let Some(eq) = self.pending_equalities.pop() {
991 let key = if eq.lhs < eq.rhs {
993 (eq.lhs, eq.rhs)
994 } else {
995 (eq.rhs, eq.lhs)
996 };
997
998 if self.processed_equalities.contains_key(&key) {
999 continue;
1000 }
1001 self.processed_equalities.insert(key, true);
1002
1003 let lhs_node = self.euf.intern(eq.lhs);
1005 let rhs_node = self.euf.intern(eq.rhs);
1006 if let Err(_e) = self
1007 .euf
1008 .merge(lhs_node, rhs_node, eq.reason.unwrap_or(eq.lhs))
1009 {
1010 continue;
1012 }
1013
1014 if let Some(conflict_terms) = self.euf.check_conflicts() {
1016 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1017 return TheoryCheckResult::Conflict(conflict_lits);
1018 }
1019
1020 self.arith.notify_equality(eq);
1022 }
1023
1024 TheoryCheckResult::Sat
1025 }
1026
1027 #[allow(dead_code)]
1031 fn model_based_combination(&mut self) -> TheoryCheckResult {
1032 let mut shared_terms: Vec<TermId> = Vec::new();
1034
1035 for &term in self.term_to_var.keys() {
1038 shared_terms.push(term);
1039 }
1040
1041 if shared_terms.len() < 2 {
1042 return TheoryCheckResult::Sat;
1043 }
1044
1045 for i in 0..shared_terms.len() {
1048 for j in (i + 1)..shared_terms.len() {
1049 let t1 = shared_terms[i];
1050 let t2 = shared_terms[j];
1051
1052 let t1_node = self.euf.intern(t1);
1054 let t2_node = self.euf.intern(t2);
1055
1056 if self.euf.are_equal(t1_node, t2_node) {
1057 let t1_value = self.arith.value(t1);
1060 let t2_value = self.arith.value(t2);
1061
1062 if let (Some(v1), Some(v2)) = (t1_value, t2_value)
1063 && v1 != v2
1064 {
1065 let conflict_lits = self.terms_to_conflict_clause(&[t1, t2]);
1069 return TheoryCheckResult::Conflict(conflict_lits);
1070 }
1071 }
1072 }
1073 }
1074
1075 TheoryCheckResult::Sat
1076 }
1077
1078 #[allow(dead_code)]
1080 fn add_shared_equality(&mut self, lhs: TermId, rhs: TermId, reason: Option<TermId>) {
1081 self.pending_equalities
1082 .push(EqualityNotification { lhs, rhs, reason });
1083 }
1084
1085 #[allow(dead_code)]
1088 fn get_decision_hints(&mut self) -> &[TheoryDecision] {
1089 self.decision_hints.clear();
1091
1092 &self.decision_hints
1103 }
1104
1105 fn terms_to_conflict_clause(&self, terms: &[TermId]) -> SmallVec<[Lit; 8]> {
1108 let mut conflict = SmallVec::new();
1109 for &term in terms {
1110 if let Some(&var) = self.term_to_var.get(&term) {
1111 conflict.push(Lit::neg(var));
1113 }
1114 }
1115 conflict
1116 }
1117
1118 fn process_constraint(
1120 &mut self,
1121 var: Var,
1122 constraint: Constraint,
1123 is_positive: bool,
1124 manager: &TermManager,
1125 ) -> TheoryCheckResult {
1126 match constraint {
1127 Constraint::Eq(lhs, rhs) => {
1128 if is_positive {
1129 let lhs_node = self.euf.intern(lhs);
1131 let rhs_node = self.euf.intern(rhs);
1132 if let Err(_e) = self.euf.merge(lhs_node, rhs_node, lhs) {
1133 return TheoryCheckResult::Sat;
1135 }
1136
1137 if let Some(conflict_terms) = self.euf.check_conflicts() {
1139 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1141 return TheoryCheckResult::Conflict(conflict_lits);
1142 }
1143
1144 if let Some(parsed) = self.var_to_parsed_arith.get(&var) {
1147 let terms: Vec<(TermId, Rational64)> =
1148 parsed.terms.iter().copied().collect();
1149 let constant = parsed.constant;
1150 let reason = parsed.reason_term;
1151
1152 self.arith.assert_eq(&terms, constant, reason);
1156
1157 use oxiz_theories::Theory;
1159 use oxiz_theories::TheoryCheckResult as TheoryCheckResultEnum;
1160 if let Ok(TheoryCheckResultEnum::Unsat(conflict_terms)) = self.arith.check()
1161 {
1162 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1163 return TheoryCheckResult::Conflict(conflict_lits);
1164 }
1165 }
1166
1167 let lhs_is_bv = manager
1171 .get(lhs)
1172 .and_then(|t| manager.sorts.get(t.sort))
1173 .is_some_and(|s| s.is_bitvec());
1174 let rhs_is_bv = manager
1175 .get(rhs)
1176 .and_then(|t| manager.sorts.get(t.sort))
1177 .is_some_and(|s| s.is_bitvec());
1178
1179 if lhs_is_bv || rhs_is_bv {
1180 let mut did_assert = false;
1181
1182 let get_bv_const = |term_id: TermId| -> Option<(u64, u32)> {
1184 manager.get(term_id).and_then(|t| match &t.kind {
1185 TermKind::BitVecConst { value, width } => {
1186 let val_u64 = value.iter_u64_digits().next().unwrap_or(0);
1187 Some((val_u64, *width))
1188 }
1189 _ => None,
1190 })
1191 };
1192
1193 let get_bv_width = |term_id: TermId| -> Option<u32> {
1195 manager.get(term_id).and_then(|t| {
1196 manager.sorts.get(t.sort).and_then(|s| s.bitvec_width())
1197 })
1198 };
1199
1200 let is_var = |term_id: TermId| -> bool {
1202 manager
1203 .get(term_id)
1204 .is_some_and(|t| matches!(t.kind, TermKind::Var(_)))
1205 };
1206
1207 let encode_bv_op =
1210 |bv: &mut BvSolver, op_term: TermId, mgr: &TermManager| -> bool {
1211 let term = match mgr.get(op_term) {
1212 Some(t) => t,
1213 None => return false,
1214 };
1215 let width = mgr.sorts.get(term.sort).and_then(|s| s.bitvec_width());
1216 let width = match width {
1217 Some(w) => w,
1218 None => return false,
1219 };
1220
1221 match &term.kind {
1222 TermKind::BvAdd(a, b) => {
1223 bv.new_bv(*a, width);
1225 bv.new_bv(*b, width);
1226 bv.bv_add(op_term, *a, *b);
1227 true
1228 }
1229 TermKind::BvMul(a, b) => {
1230 bv.new_bv(*a, width);
1231 bv.new_bv(*b, width);
1232 bv.bv_mul(op_term, *a, *b);
1233 true
1234 }
1235 TermKind::BvSub(a, b) => {
1236 bv.new_bv(*a, width);
1237 bv.new_bv(*b, width);
1238 bv.bv_sub(op_term, *a, *b);
1239 true
1240 }
1241 TermKind::BvAnd(a, b) => {
1242 bv.new_bv(*a, width);
1243 bv.new_bv(*b, width);
1244 bv.bv_and(op_term, *a, *b);
1245 true
1246 }
1247 TermKind::BvOr(a, b) => {
1248 bv.new_bv(*a, width);
1249 bv.new_bv(*b, width);
1250 bv.bv_or(op_term, *a, *b);
1251 true
1252 }
1253 TermKind::BvXor(a, b) => {
1254 bv.new_bv(*a, width);
1255 bv.new_bv(*b, width);
1256 bv.bv_xor(op_term, *a, *b);
1257 true
1258 }
1259 TermKind::BvNot(a) => {
1260 bv.new_bv(*a, width);
1261 bv.bv_not(op_term, *a);
1262 true
1263 }
1264 TermKind::BvUdiv(a, b) => {
1265 bv.new_bv(*a, width);
1266 bv.new_bv(*b, width);
1267 bv.bv_udiv(op_term, *a, *b);
1268 true
1269 }
1270 TermKind::BvSdiv(a, b) => {
1271 bv.new_bv(*a, width);
1272 bv.new_bv(*b, width);
1273 bv.bv_sdiv(op_term, *a, *b);
1274 true
1275 }
1276 TermKind::BvUrem(a, b) => {
1277 bv.new_bv(*a, width);
1278 bv.new_bv(*b, width);
1279 bv.bv_urem(op_term, *a, *b);
1280 true
1281 }
1282 TermKind::BvSrem(a, b) => {
1283 bv.new_bv(*a, width);
1284 bv.new_bv(*b, width);
1285 bv.bv_srem(op_term, *a, *b);
1286 true
1287 }
1288 TermKind::Var(_) => {
1289 bv.new_bv(op_term, width);
1291 true
1292 }
1293 _ => false,
1294 }
1295 };
1296
1297 let lhs_term = manager.get(lhs);
1299 let rhs_term = manager.get(rhs);
1300
1301 let is_bv_op = |t: &oxiz_core::ast::Term| {
1303 matches!(
1304 t.kind,
1305 TermKind::BvAdd(_, _)
1306 | TermKind::BvMul(_, _)
1307 | TermKind::BvSub(_, _)
1308 | TermKind::BvAnd(_, _)
1309 | TermKind::BvOr(_, _)
1310 | TermKind::BvXor(_, _)
1311 | TermKind::BvNot(_)
1312 | TermKind::BvUdiv(_, _)
1313 | TermKind::BvSdiv(_, _)
1314 | TermKind::BvUrem(_, _)
1315 | TermKind::BvSrem(_, _)
1316 )
1317 };
1318
1319 let lhs_is_op = lhs_term.is_some_and(is_bv_op);
1320 let rhs_is_op = rhs_term.is_some_and(is_bv_op);
1321
1322 let lhs_const_info = get_bv_const(lhs);
1323 let rhs_const_info = get_bv_const(rhs);
1324 let lhs_is_var = is_var(lhs);
1325 let rhs_is_var = is_var(rhs);
1326
1327 if lhs_is_op {
1329 if let Some(width) = get_bv_width(lhs) {
1330 let _encoded = encode_bv_op(self.bv, lhs, manager);
1332
1333 if let Some((val, _)) = rhs_const_info {
1334 self.bv.assert_const(lhs, val, width);
1336 did_assert = true;
1337 } else if rhs_is_var && self.bv_terms.contains(&rhs) {
1338 self.bv.new_bv(rhs, width);
1340 self.bv.assert_eq(lhs, rhs);
1341 did_assert = true;
1342 }
1343 }
1344 }
1345 else if rhs_is_op {
1347 if let Some(width) = get_bv_width(rhs) {
1348 encode_bv_op(self.bv, rhs, manager);
1350
1351 if let Some((val, _)) = lhs_const_info {
1352 self.bv.assert_const(rhs, val, width);
1354 did_assert = true;
1355 } else if lhs_is_var && self.bv_terms.contains(&lhs) {
1356 self.bv.new_bv(lhs, width);
1358 self.bv.assert_eq(lhs, rhs);
1359 did_assert = true;
1360 }
1361 }
1362 }
1363 else if lhs_is_var && self.bv_terms.contains(&lhs) {
1365 if let Some((val, width)) = rhs_const_info {
1366 self.bv.assert_const(lhs, val, width);
1367 did_assert = true;
1368 }
1369 }
1370 else if rhs_is_var && self.bv_terms.contains(&rhs) {
1372 if let Some((val, width)) = lhs_const_info {
1373 self.bv.assert_const(rhs, val, width);
1374 did_assert = true;
1375 }
1376 }
1377 else if lhs_is_var
1379 && rhs_is_var
1380 && self.bv_terms.contains(&lhs)
1381 && self.bv_terms.contains(&rhs)
1382 && let Some(width) = get_bv_width(lhs)
1383 {
1384 self.bv.new_bv(lhs, width);
1385 self.bv.new_bv(rhs, width);
1386 self.bv.assert_eq(lhs, rhs);
1387 did_assert = true;
1388 }
1389
1390 if did_assert && self.has_bv_arith_ops {
1397 use oxiz_theories::Theory;
1398 use oxiz_theories::TheoryCheckResult as TheoryCheckResultEnum;
1399 if let Ok(TheoryCheckResultEnum::Unsat(conflict_terms)) =
1400 self.bv.check()
1401 {
1402 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1403 return TheoryCheckResult::Conflict(conflict_lits);
1404 }
1405 }
1406 }
1407 } else {
1408 let lhs_node = self.euf.intern(lhs);
1410 let rhs_node = self.euf.intern(rhs);
1411 self.euf.assert_diseq(lhs_node, rhs_node, lhs);
1412
1413 if let Some(conflict_terms) = self.euf.check_conflicts() {
1415 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1416 return TheoryCheckResult::Conflict(conflict_lits);
1417 }
1418 }
1419 }
1420 Constraint::Diseq(lhs, rhs) => {
1421 if is_positive {
1422 let lhs_node = self.euf.intern(lhs);
1424 let rhs_node = self.euf.intern(rhs);
1425 self.euf.assert_diseq(lhs_node, rhs_node, lhs);
1426
1427 if let Some(conflict_terms) = self.euf.check_conflicts() {
1428 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1429 return TheoryCheckResult::Conflict(conflict_lits);
1430 }
1431 } else {
1432 let lhs_node = self.euf.intern(lhs);
1434 let rhs_node = self.euf.intern(rhs);
1435 if let Err(_e) = self.euf.merge(lhs_node, rhs_node, lhs) {
1436 return TheoryCheckResult::Sat;
1437 }
1438
1439 if let Some(conflict_terms) = self.euf.check_conflicts() {
1440 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1441 return TheoryCheckResult::Conflict(conflict_lits);
1442 }
1443 }
1444 }
1445 Constraint::Lt(lhs, rhs)
1447 | Constraint::Le(lhs, rhs)
1448 | Constraint::Gt(lhs, rhs)
1449 | Constraint::Ge(lhs, rhs) => {
1450 let lhs_is_bv = self.bv_terms.contains(&lhs);
1452 let rhs_is_bv = self.bv_terms.contains(&rhs);
1453
1454 if lhs_is_bv || rhs_is_bv {
1456 let width = manager
1458 .get(lhs)
1459 .and_then(|t| manager.sorts.get(t.sort).and_then(|s| s.bitvec_width()));
1460
1461 if let Some(width) = width {
1462 self.bv.new_bv(lhs, width);
1464 self.bv.new_bv(rhs, width);
1465
1466 let is_signed = false;
1471
1472 if is_positive {
1473 match constraint {
1475 Constraint::Lt(a, b) => {
1476 if is_signed {
1477 self.bv.assert_slt(a, b);
1478 } else {
1479 self.bv.assert_ult(a, b);
1480 }
1481 }
1482 Constraint::Le(a, b) => {
1483 if is_signed {
1484 self.bv.assert_sle(a, b);
1485 } else {
1486 }
1490 }
1491 _ => {}
1492 }
1493 }
1494
1495 use oxiz_theories::Theory;
1497 use oxiz_theories::TheoryCheckResult as TheoryCheckResultEnum;
1498 if let Ok(TheoryCheckResultEnum::Unsat(conflict_terms)) = self.bv.check() {
1499 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1500 return TheoryCheckResult::Conflict(conflict_lits);
1501 }
1502 }
1503 }
1504
1505 if let Some(parsed) = self.var_to_parsed_arith.get(&var) {
1507 let terms: Vec<(TermId, Rational64)> = parsed.terms.iter().copied().collect();
1509 let reason = parsed.reason_term;
1510 let constant = parsed.constant;
1511
1512 if is_positive {
1513 match parsed.constraint_type {
1515 ArithConstraintType::Lt => {
1516 self.arith.assert_lt(&terms, constant, reason);
1518 }
1519 ArithConstraintType::Le => {
1520 self.arith.assert_le(&terms, constant, reason);
1522 }
1523 ArithConstraintType::Gt => {
1524 self.arith.assert_gt(&terms, constant, reason);
1526 }
1527 ArithConstraintType::Ge => {
1528 self.arith.assert_ge(&terms, constant, reason);
1530 }
1531 }
1532 } else {
1533 match parsed.constraint_type {
1539 ArithConstraintType::Lt => {
1540 self.arith.assert_ge(&terms, constant, reason);
1542 }
1543 ArithConstraintType::Le => {
1544 self.arith.assert_gt(&terms, constant, reason);
1546 }
1547 ArithConstraintType::Gt => {
1548 self.arith.assert_le(&terms, constant, reason);
1550 }
1551 ArithConstraintType::Ge => {
1552 self.arith.assert_lt(&terms, constant, reason);
1554 }
1555 }
1556 }
1557
1558 use oxiz_theories::Theory;
1560 use oxiz_theories::TheoryCheckResult as TheoryCheckResultEnum;
1561 if let Ok(TheoryCheckResultEnum::Unsat(conflict_terms)) = self.arith.check() {
1562 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1563 return TheoryCheckResult::Conflict(conflict_lits);
1564 }
1565 }
1566 }
1567 }
1568 TheoryCheckResult::Sat
1569 }
1570}
1571
1572impl TheoryCallback for TheoryManager<'_> {
1573 fn on_assignment(&mut self, lit: Lit) -> TheoryCheckResult {
1574 let var = lit.var();
1575 let is_positive = !lit.is_neg();
1576
1577 self.statistics.propagations += 1;
1579
1580 if self.theory_mode == TheoryMode::Lazy {
1582 if self.var_to_constraint.contains_key(&var) {
1584 self.pending_assignments.push((lit, is_positive));
1585 }
1586 return TheoryCheckResult::Sat;
1587 }
1588
1589 let Some(constraint) = self.var_to_constraint.get(&var).cloned() else {
1592 return TheoryCheckResult::Sat;
1593 };
1594
1595 self.processed_count += 1;
1596 self.statistics.theory_propagations += 1;
1597
1598 let result = self.process_constraint(var, constraint, is_positive, self.manager);
1599
1600 if matches!(result, TheoryCheckResult::Conflict(_)) {
1602 self.statistics.theory_conflicts += 1;
1603 self.statistics.conflicts += 1;
1604
1605 if self.max_conflicts > 0 && self.statistics.conflicts >= self.max_conflicts {
1607 return TheoryCheckResult::Sat; }
1609 }
1610
1611 result
1612 }
1613
1614 fn final_check(&mut self) -> TheoryCheckResult {
1615 if self.theory_mode == TheoryMode::Lazy {
1617 for &(lit, is_positive) in &self.pending_assignments.clone() {
1618 let var = lit.var();
1619 let Some(constraint) = self.var_to_constraint.get(&var).cloned() else {
1620 continue;
1621 };
1622
1623 self.statistics.theory_propagations += 1;
1624
1625 let result = self.process_constraint(var, constraint, is_positive, self.manager);
1627 if let TheoryCheckResult::Conflict(conflict) = result {
1628 self.statistics.theory_conflicts += 1;
1629 self.statistics.conflicts += 1;
1630
1631 if self.max_conflicts > 0 && self.statistics.conflicts >= self.max_conflicts {
1633 return TheoryCheckResult::Sat; }
1635
1636 return TheoryCheckResult::Conflict(conflict);
1637 }
1638 }
1639 self.pending_assignments.clear();
1641 }
1642
1643 if let Some(conflict_terms) = self.euf.check_conflicts() {
1645 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1647 self.statistics.theory_conflicts += 1;
1648 self.statistics.conflicts += 1;
1649
1650 if self.max_conflicts > 0 && self.statistics.conflicts >= self.max_conflicts {
1652 return TheoryCheckResult::Sat; }
1654
1655 return TheoryCheckResult::Conflict(conflict_lits);
1656 }
1657
1658 match self.arith.check() {
1660 Ok(result) => {
1661 match result {
1662 oxiz_theories::TheoryCheckResult::Sat => {
1663 self.model_based_combination()
1666 }
1667 oxiz_theories::TheoryCheckResult::Unsat(conflict_terms) => {
1668 let conflict_lits = self.terms_to_conflict_clause(&conflict_terms);
1670 self.statistics.theory_conflicts += 1;
1671 self.statistics.conflicts += 1;
1672
1673 if self.max_conflicts > 0 && self.statistics.conflicts >= self.max_conflicts
1675 {
1676 return TheoryCheckResult::Sat; }
1678
1679 TheoryCheckResult::Conflict(conflict_lits)
1680 }
1681 oxiz_theories::TheoryCheckResult::Propagate(_) => {
1682 self.model_based_combination()
1684 }
1685 oxiz_theories::TheoryCheckResult::Unknown => {
1686 TheoryCheckResult::Sat
1688 }
1689 }
1690 }
1691 Err(_error) => {
1692 TheoryCheckResult::Sat
1695 }
1696 }
1697 }
1698
1699 fn on_new_level(&mut self, level: u32) {
1700 while self.level_stack.len() < (level as usize + 1) {
1703 self.level_stack.push(self.processed_count);
1704 self.euf.push();
1705 self.arith.push();
1706 self.bv.push();
1707 }
1708 }
1709
1710 fn on_backtrack(&mut self, level: u32) {
1711 while self.level_stack.len() > (level as usize + 1) {
1713 self.level_stack.pop();
1714 self.euf.pop();
1715 self.arith.pop();
1716 self.bv.pop();
1717 }
1718 self.processed_count = *self.level_stack.last().unwrap_or(&0);
1719
1720 if self.theory_mode == TheoryMode::Lazy {
1722 self.pending_assignments.clear();
1723 }
1724 }
1725}
1726
1727#[derive(Debug, Clone)]
1729enum TrailOp {
1730 AssertionAdded { index: usize },
1732 VarCreated {
1734 #[allow(dead_code)]
1735 var: Var,
1736 term: TermId,
1737 },
1738 ConstraintAdded { var: Var },
1740 FalseAssertionSet,
1742 NamedAssertionAdded { index: usize },
1744 BvTermAdded { term: TermId },
1746 ArithTermAdded { term: TermId },
1748}
1749
1750#[derive(Debug, Clone)]
1752struct ContextState {
1753 num_assertions: usize,
1754 num_vars: usize,
1755 has_false_assertion: bool,
1756 trail_position: usize,
1758}
1759
1760#[derive(Debug, Default)]
1762struct FpConstraintCollector {
1763 is_zero_vars: FxHashSet<TermId>,
1765 is_negative_vars: FxHashSet<TermId>,
1767 is_positive_vars: FxHashSet<TermId>,
1769 fp_adds: Vec<(TermKind, TermId, TermId, TermId)>,
1771 fp_lts: Vec<(TermId, TermId)>,
1773 fp_divs: Vec<(TermKind, TermId, TermId, TermId)>,
1775 fp_muls: Vec<(TermKind, TermId, TermId, TermId)>,
1777 equalities: Vec<(TermId, TermId)>,
1779 fp_conversions: Vec<(TermId, u32, u32, TermId)>,
1781 real_to_fp: Vec<(TermKind, TermId, u32, u32, TermId)>,
1783}
1784
1785impl FpConstraintCollector {
1786 fn new() -> Self {
1787 Self::default()
1788 }
1789
1790 fn collect(&mut self, term: TermId, manager: &TermManager) {
1791 let Some(term_data) = manager.get(term) else {
1792 return;
1793 };
1794
1795 match &term_data.kind {
1796 TermKind::FpIsZero(arg) => {
1798 self.is_zero_vars.insert(*arg);
1799 self.collect(*arg, manager);
1800 }
1801 TermKind::FpIsNegative(arg) => {
1802 self.is_negative_vars.insert(*arg);
1803 self.collect(*arg, manager);
1804 }
1805 TermKind::FpIsPositive(arg) => {
1806 self.is_positive_vars.insert(*arg);
1807 self.collect(*arg, manager);
1808 }
1809 TermKind::FpLt(lhs, rhs) => {
1811 self.fp_lts.push((*lhs, *rhs));
1812 self.collect(*lhs, manager);
1813 self.collect(*rhs, manager);
1814 }
1815 TermKind::Eq(lhs, rhs) => {
1817 self.equalities.push((*lhs, *rhs));
1818 self.collect(*lhs, manager);
1819 self.collect(*rhs, manager);
1820 }
1821 TermKind::FpAdd(rm, lhs, rhs) => {
1823 self.fp_adds
1824 .push((TermKind::FpAdd(*rm, *lhs, *rhs), *lhs, *rhs, term));
1825 self.collect(*lhs, manager);
1826 self.collect(*rhs, manager);
1827 }
1828 TermKind::FpDiv(rm, lhs, rhs) => {
1829 self.fp_divs
1830 .push((TermKind::FpDiv(*rm, *lhs, *rhs), *lhs, *rhs, term));
1831 self.collect(*lhs, manager);
1832 self.collect(*rhs, manager);
1833 }
1834 TermKind::FpMul(rm, lhs, rhs) => {
1835 self.fp_muls
1836 .push((TermKind::FpMul(*rm, *lhs, *rhs), *lhs, *rhs, term));
1837 self.collect(*lhs, manager);
1838 self.collect(*rhs, manager);
1839 }
1840 TermKind::FpToFp { rm: _, arg, eb, sb } => {
1842 self.fp_conversions.push((*arg, *eb, *sb, term));
1843 self.collect(*arg, manager);
1844 }
1845 TermKind::RealToFp { rm, arg, eb, sb } => {
1846 self.real_to_fp.push((
1847 TermKind::RealToFp {
1848 rm: *rm,
1849 arg: *arg,
1850 eb: *eb,
1851 sb: *sb,
1852 },
1853 *arg,
1854 *eb,
1855 *sb,
1856 term,
1857 ));
1858 self.collect(*arg, manager);
1859 }
1860 TermKind::And(args) => {
1862 for &arg in args {
1863 self.collect(arg, manager);
1864 }
1865 }
1866 TermKind::Or(args) => {
1867 for &arg in args {
1868 self.collect(arg, manager);
1869 }
1870 }
1871 TermKind::Not(inner) => {
1872 self.collect(*inner, manager);
1873 }
1874 TermKind::Implies(lhs, rhs) => {
1875 self.collect(*lhs, manager);
1876 self.collect(*rhs, manager);
1877 }
1878 _ => {}
1879 }
1880 }
1881
1882 fn check_conflicts(&self, manager: &TermManager) -> bool {
1883 for &var in &self.is_zero_vars {
1887 if self.is_negative_vars.contains(&var) {
1888 if self.is_positive_zero_plus_negative_zero_result(var, manager) {
1890 return true; }
1892 }
1893 }
1894
1895 if self.check_rounding_mode_conflict(manager) {
1899 return true;
1900 }
1901
1902 if self.check_non_associativity_conflict(manager) {
1905 return true;
1906 }
1907
1908 if self.check_precision_loss_conflict(manager) {
1911 return true;
1912 }
1913
1914 false
1915 }
1916
1917 fn is_positive_zero_plus_negative_zero_result(
1918 &self,
1919 var: TermId,
1920 manager: &TermManager,
1921 ) -> bool {
1922 for &(lhs, rhs) in &self.equalities {
1924 if lhs == var {
1925 if self.is_zero_addition_of_opposite_signs(rhs, manager) {
1926 return true;
1927 }
1928 }
1929 if rhs == var {
1930 if self.is_zero_addition_of_opposite_signs(lhs, manager) {
1931 return true;
1932 }
1933 }
1934 }
1935 false
1936 }
1937
1938 fn is_zero_addition_of_opposite_signs(&self, term: TermId, manager: &TermManager) -> bool {
1939 let Some(term_data) = manager.get(term) else {
1940 return false;
1941 };
1942
1943 if let TermKind::FpAdd(_, lhs, rhs) = &term_data.kind {
1944 let lhs_is_pos_zero =
1946 self.is_zero_vars.contains(lhs) && self.is_positive_vars.contains(lhs);
1947 let lhs_is_neg_zero =
1948 self.is_zero_vars.contains(lhs) && self.is_negative_vars.contains(lhs);
1949 let rhs_is_pos_zero =
1950 self.is_zero_vars.contains(rhs) && self.is_positive_vars.contains(rhs);
1951 let rhs_is_neg_zero =
1952 self.is_zero_vars.contains(rhs) && self.is_negative_vars.contains(rhs);
1953
1954 (lhs_is_pos_zero && rhs_is_neg_zero) || (lhs_is_neg_zero && rhs_is_pos_zero)
1956 } else {
1957 false
1958 }
1959 }
1960
1961 fn check_rounding_mode_conflict(&self, manager: &TermManager) -> bool {
1962 for &(lt_lhs, lt_rhs) in &self.fp_lts {
1965 let lhs_data = manager.get(lt_lhs);
1967 let rhs_data = manager.get(lt_rhs);
1968
1969 if let (Some(lhs), Some(rhs)) = (lhs_data, rhs_data) {
1970 if let (TermKind::FpAdd(rm_lhs, a1, b1), TermKind::FpAdd(rm_rhs, a2, b2)) =
1971 (&lhs.kind, &rhs.kind)
1972 {
1973 if *rm_lhs == RoundingMode::RTP
1975 && *rm_rhs == RoundingMode::RTN
1976 && a1 == a2
1977 && b1 == b2
1978 {
1979 return true;
1980 }
1981 }
1982 }
1983 }
1984 false
1985 }
1986
1987 fn check_non_associativity_conflict(&self, manager: &TermManager) -> bool {
1988 for &(_, div_lhs, div_rhs, div_result) in &self.fp_divs {
1991 for &(_, mul_lhs, mul_rhs, mul_result) in &self.fp_muls {
1992 if mul_lhs == div_result || mul_rhs == div_result {
1994 let other_mul_operand = if mul_lhs == div_result {
1996 mul_rhs
1997 } else {
1998 mul_lhs
1999 };
2000
2001 if self.terms_equal(other_mul_operand, div_rhs, manager) {
2003 for &(eq_lhs, eq_rhs) in &self.equalities {
2005 if (eq_lhs == mul_result && self.terms_equal(eq_rhs, div_lhs, manager))
2006 || (eq_rhs == mul_result
2007 && self.terms_equal(eq_lhs, div_lhs, manager))
2008 {
2009 if self.is_non_exact_division(div_lhs, div_rhs, manager) {
2013 return true;
2014 }
2015 }
2016 }
2017 }
2018 }
2019 }
2020 }
2021 false
2022 }
2023
2024 fn terms_equal(&self, a: TermId, b: TermId, manager: &TermManager) -> bool {
2025 if a == b {
2026 return true;
2027 }
2028 for &(eq_lhs, eq_rhs) in &self.equalities {
2030 if (eq_lhs == a && eq_rhs == b) || (eq_lhs == b && eq_rhs == a) {
2031 return true;
2032 }
2033 }
2034 false
2035 }
2036
2037 fn is_non_exact_division(
2038 &self,
2039 dividend: TermId,
2040 divisor: TermId,
2041 manager: &TermManager,
2042 ) -> bool {
2043 if let Some(div_val) = self.get_fp_literal_value(dividend, manager) {
2046 if let Some(divisor_val) = self.get_fp_literal_value(divisor, manager) {
2047 if divisor_val != 0.0 {
2049 let quotient = div_val / divisor_val;
2050 let product = quotient * divisor_val;
2051 if (product - div_val).abs() > f64::EPSILON {
2053 return true;
2054 }
2055 }
2056 }
2057 }
2058 false
2059 }
2060
2061 fn get_fp_literal_value(&self, term: TermId, manager: &TermManager) -> Option<f64> {
2062 for &(eq_lhs, eq_rhs) in &self.equalities {
2065 if eq_lhs == term {
2066 if let Some(val) = self.extract_fp_value(eq_rhs, manager) {
2067 return Some(val);
2068 }
2069 }
2070 if eq_rhs == term {
2071 if let Some(val) = self.extract_fp_value(eq_lhs, manager) {
2072 return Some(val);
2073 }
2074 }
2075 }
2076 self.extract_fp_value(term, manager)
2077 }
2078
2079 fn extract_fp_value(&self, term: TermId, manager: &TermManager) -> Option<f64> {
2080 let term_data = manager.get(term)?;
2081 match &term_data.kind {
2082 TermKind::RealToFp { arg, .. } => {
2083 if let Some(real_data) = manager.get(*arg) {
2085 if let TermKind::RealConst(r) = &real_data.kind {
2086 return r.to_f64();
2087 }
2088 }
2089 None
2090 }
2091 TermKind::IntConst(n) => n.to_i64().map(|v| v as f64),
2092 TermKind::RealConst(r) => r.to_f64(),
2093 _ => None,
2094 }
2095 }
2096
2097 fn check_precision_loss_conflict(&self, manager: &TermManager) -> bool {
2098 for i in 0..self.fp_conversions.len() {
2103 for j in i + 1..self.fp_conversions.len() {
2104 let (src1, eb1, sb1, result1) = self.fp_conversions[i];
2105 let (src2, eb2, sb2, result2) = self.fp_conversions[j];
2106
2107 if eb1 == eb2 && sb1 == sb2 {
2109 if self.terms_equal(result1, result2, manager) {
2111 if self.source_went_through_smaller_format(src1, eb1, sb1, manager)
2113 && self.is_direct_from_value(src2, manager)
2114 {
2115 if self.value_loses_precision_in_smaller_format(src2, manager) {
2117 return true;
2118 }
2119 }
2120 if self.source_went_through_smaller_format(src2, eb2, sb2, manager)
2121 && self.is_direct_from_value(src1, manager)
2122 {
2123 if self.value_loses_precision_in_smaller_format(src1, manager) {
2124 return true;
2125 }
2126 }
2127 }
2128 }
2129 }
2130 }
2131 false
2132 }
2133
2134 fn source_went_through_smaller_format(
2135 &self,
2136 source: TermId,
2137 target_eb: u32,
2138 target_sb: u32,
2139 manager: &TermManager,
2140 ) -> bool {
2141 if let Some(term_data) = manager.get(source) {
2143 if let TermKind::FpToFp { arg: _, eb, sb, .. } = &term_data.kind {
2144 return *eb < target_eb || *sb < target_sb;
2146 }
2147 }
2148 for &(eq_lhs, eq_rhs) in &self.equalities {
2150 let to_check = if eq_lhs == source {
2151 eq_rhs
2152 } else if eq_rhs == source {
2153 eq_lhs
2154 } else {
2155 continue;
2156 };
2157 if let Some(term_data) = manager.get(to_check) {
2158 if let TermKind::FpToFp { arg: _, eb, sb, .. } = &term_data.kind {
2159 return *eb < target_eb || *sb < target_sb;
2160 }
2161 }
2162 }
2163 false
2164 }
2165
2166 fn is_direct_from_value(&self, term: TermId, manager: &TermManager) -> bool {
2167 if let Some(term_data) = manager.get(term) {
2169 if matches!(term_data.kind, TermKind::RealToFp { .. }) {
2170 return true;
2171 }
2172 }
2173 for &(eq_lhs, eq_rhs) in &self.equalities {
2174 let to_check = if eq_lhs == term {
2175 eq_rhs
2176 } else if eq_rhs == term {
2177 eq_lhs
2178 } else {
2179 continue;
2180 };
2181 if let Some(term_data) = manager.get(to_check) {
2182 if matches!(term_data.kind, TermKind::RealToFp { .. }) {
2183 return true;
2184 }
2185 }
2186 }
2187 false
2188 }
2189
2190 fn value_loses_precision_in_smaller_format(&self, term: TermId, manager: &TermManager) -> bool {
2191 if let Some(val) = self.get_original_real_value(term, manager) {
2193 let as_f32 = val as f32;
2195 let back_to_f64 = as_f32 as f64;
2196 if (val - back_to_f64).abs() > f64::EPSILON {
2197 return true;
2198 }
2199 }
2200 false
2201 }
2202
2203 fn get_original_real_value(&self, term: TermId, manager: &TermManager) -> Option<f64> {
2204 if let Some(term_data) = manager.get(term) {
2206 if let TermKind::RealToFp { arg, .. } = &term_data.kind {
2207 if let Some(arg_data) = manager.get(*arg) {
2208 if let TermKind::RealConst(r) = &arg_data.kind {
2209 return r.to_f64();
2210 }
2211 }
2212 }
2213 }
2214 for &(eq_lhs, eq_rhs) in &self.equalities {
2215 let to_check = if eq_lhs == term {
2216 eq_rhs
2217 } else if eq_rhs == term {
2218 eq_lhs
2219 } else {
2220 continue;
2221 };
2222 if let Some(term_data) = manager.get(to_check) {
2223 if let TermKind::RealToFp { arg, .. } = &term_data.kind {
2224 if let Some(arg_data) = manager.get(*arg) {
2225 if let TermKind::RealConst(r) = &arg_data.kind {
2226 return r.to_f64();
2227 }
2228 }
2229 }
2230 }
2231 }
2232 None
2233 }
2234}
2235
2236impl Default for Solver {
2237 fn default() -> Self {
2238 Self::new()
2239 }
2240}
2241
2242impl Solver {
2243 #[must_use]
2245 pub fn new() -> Self {
2246 Self::with_config(SolverConfig::default())
2247 }
2248
2249 #[must_use]
2251 pub fn with_config(config: SolverConfig) -> Self {
2252 let proof_enabled = config.proof;
2253
2254 let sat_config = SatConfig {
2256 restart_strategy: config.restart_strategy,
2257 enable_inprocessing: config.enable_inprocessing,
2258 inprocessing_interval: config.inprocessing_interval,
2259 ..SatConfig::default()
2260 };
2261
2262 Self {
2272 config,
2273 sat: SatSolver::with_config(sat_config),
2274 euf: EufSolver::new(),
2275 arith: ArithSolver::lra(),
2276 bv: BvSolver::new(),
2277 nlsat: None,
2278 mbqi: MBQIIntegration::new(),
2279 has_quantifiers: false,
2280 term_to_var: FxHashMap::default(),
2281 var_to_term: Vec::new(),
2282 var_to_constraint: FxHashMap::default(),
2283 var_to_parsed_arith: FxHashMap::default(),
2284 logic: None,
2285 assertions: Vec::new(),
2286 named_assertions: Vec::new(),
2287 assumption_vars: FxHashMap::default(),
2288 model: None,
2289 unsat_core: None,
2290 context_stack: Vec::new(),
2291 trail: Vec::new(),
2292 theory_processed_up_to: 0,
2293 produce_unsat_cores: false,
2294 has_false_assertion: false,
2295 polarities: FxHashMap::default(),
2296 polarity_aware: true, theory_aware_branching: true, proof: if proof_enabled {
2299 Some(Proof::new())
2300 } else {
2301 None
2302 },
2303 simplifier: Simplifier::new(),
2304 statistics: Statistics::new(),
2305 bv_terms: FxHashSet::default(),
2306 has_bv_arith_ops: false,
2307 arith_terms: FxHashSet::default(),
2308 dt_var_constructors: FxHashMap::default(),
2309 }
2310 }
2311
2312 #[must_use]
2314 pub fn get_proof(&self) -> Option<&Proof> {
2315 self.proof.as_ref()
2316 }
2317
2318 #[must_use]
2320 pub fn get_statistics(&self) -> &Statistics {
2321 &self.statistics
2322 }
2323
2324 pub fn reset_statistics(&mut self) {
2326 self.statistics.reset();
2327 }
2328
2329 pub fn set_theory_aware_branching(&mut self, enabled: bool) {
2331 self.theory_aware_branching = enabled;
2332 }
2333
2334 #[must_use]
2336 pub fn theory_aware_branching(&self) -> bool {
2337 self.theory_aware_branching
2338 }
2339
2340 pub fn set_produce_unsat_cores(&mut self, produce: bool) {
2342 self.produce_unsat_cores = produce;
2343 }
2344
2345 pub fn set_logic(&mut self, logic: &str) {
2347 self.logic = Some(logic.to_string());
2348
2349 if logic.contains("NIA") {
2352 self.nlsat = Some(oxiz_theories::nlsat::NlsatTheory::new(true));
2354 self.arith = ArithSolver::lia(); tracing::info!("Using NLSAT solver for QF_NIA (nonlinear integer arithmetic)");
2356 } else if logic.contains("NRA") {
2357 self.nlsat = Some(oxiz_theories::nlsat::NlsatTheory::new(false));
2359 self.arith = ArithSolver::lra(); tracing::info!("Using NLSAT solver for QF_NRA (nonlinear real arithmetic)");
2361 } else if logic.contains("LIA") || logic.contains("IDL") {
2362 self.arith = ArithSolver::lia();
2364 } else if logic.contains("LRA") || logic.contains("RDL") {
2365 self.arith = ArithSolver::lra();
2367 } else if logic.contains("BV") {
2368 self.arith = ArithSolver::lia();
2371 }
2372 }
2374
2375 fn extract_dt_var_constructor(
2378 &self,
2379 lhs: TermId,
2380 rhs: TermId,
2381 manager: &TermManager,
2382 ) -> Option<(TermId, lasso::Spur)> {
2383 let lhs_term = manager.get(lhs)?;
2384 let rhs_term = manager.get(rhs)?;
2385
2386 if matches!(lhs_term.kind, TermKind::Var(_)) {
2388 if let TermKind::DtConstructor { constructor, .. } = &rhs_term.kind {
2389 return Some((lhs, *constructor));
2390 }
2391 }
2392 if matches!(rhs_term.kind, TermKind::Var(_)) {
2394 if let TermKind::DtConstructor { constructor, .. } = &lhs_term.kind {
2395 return Some((rhs, *constructor));
2396 }
2397 }
2398 None
2399 }
2400
2401 fn collect_polarities(&mut self, term: TermId, polarity: Polarity, manager: &TermManager) {
2404 let current = self.polarities.get(&term).copied();
2406 let new_polarity = match (current, polarity) {
2407 (Some(Polarity::Both), _) | (_, Polarity::Both) => Polarity::Both,
2408 (Some(Polarity::Positive), Polarity::Negative)
2409 | (Some(Polarity::Negative), Polarity::Positive) => Polarity::Both,
2410 (Some(p), _) => p,
2411 (None, p) => p,
2412 };
2413 self.polarities.insert(term, new_polarity);
2414
2415 if current == Some(Polarity::Both) {
2417 return;
2418 }
2419
2420 let Some(t) = manager.get(term) else {
2422 return;
2423 };
2424
2425 match &t.kind {
2426 TermKind::Not(arg) => {
2427 let neg_polarity = match polarity {
2428 Polarity::Positive => Polarity::Negative,
2429 Polarity::Negative => Polarity::Positive,
2430 Polarity::Both => Polarity::Both,
2431 };
2432 self.collect_polarities(*arg, neg_polarity, manager);
2433 }
2434 TermKind::And(args) | TermKind::Or(args) => {
2435 for &arg in args {
2436 self.collect_polarities(arg, polarity, manager);
2437 }
2438 }
2439 TermKind::Implies(lhs, rhs) => {
2440 let neg_polarity = match polarity {
2441 Polarity::Positive => Polarity::Negative,
2442 Polarity::Negative => Polarity::Positive,
2443 Polarity::Both => Polarity::Both,
2444 };
2445 self.collect_polarities(*lhs, neg_polarity, manager);
2446 self.collect_polarities(*rhs, polarity, manager);
2447 }
2448 TermKind::Ite(cond, then_br, else_br) => {
2449 self.collect_polarities(*cond, Polarity::Both, manager);
2450 self.collect_polarities(*then_br, polarity, manager);
2451 self.collect_polarities(*else_br, polarity, manager);
2452 }
2453 TermKind::Xor(lhs, rhs) | TermKind::Eq(lhs, rhs) => {
2454 self.collect_polarities(*lhs, Polarity::Both, manager);
2456 self.collect_polarities(*rhs, Polarity::Both, manager);
2457 }
2458 _ => {
2459 }
2461 }
2462 }
2463
2464 fn get_or_create_var(&mut self, term: TermId) -> Var {
2466 if let Some(&var) = self.term_to_var.get(&term) {
2467 return var;
2468 }
2469
2470 let var = self.sat.new_var();
2471 self.term_to_var.insert(term, var);
2472 self.trail.push(TrailOp::VarCreated { var, term });
2473
2474 while self.var_to_term.len() <= var.index() {
2475 self.var_to_term.push(TermId::new(0));
2476 }
2477 self.var_to_term[var.index()] = term;
2478 var
2479 }
2480
2481 fn track_theory_vars(&mut self, term_id: TermId, manager: &TermManager) {
2484 let Some(term) = manager.get(term_id) else {
2485 return;
2486 };
2487
2488 match &term.kind {
2489 TermKind::Var(_) => {
2490 let is_int = term.sort == manager.sorts.int_sort;
2492 let is_real = term.sort == manager.sorts.real_sort;
2493
2494 if is_int || is_real {
2495 if !self.arith_terms.contains(&term_id) {
2496 self.arith_terms.insert(term_id);
2497 self.trail.push(TrailOp::ArithTermAdded { term: term_id });
2498 self.arith.intern(term_id);
2499 }
2500 } else if let Some(sort) = manager.sorts.get(term.sort)
2501 && sort.is_bitvec()
2502 && !self.bv_terms.contains(&term_id)
2503 {
2504 self.bv_terms.insert(term_id);
2505 self.trail.push(TrailOp::BvTermAdded { term: term_id });
2506 if let Some(width) = sort.bitvec_width() {
2507 self.bv.new_bv(term_id, width);
2508 }
2509 self.arith.intern(term_id);
2512 }
2513 }
2514 TermKind::Add(args)
2516 | TermKind::Mul(args)
2517 | TermKind::And(args)
2518 | TermKind::Or(args) => {
2519 for &arg in args {
2520 self.track_theory_vars(arg, manager);
2521 }
2522 }
2523 TermKind::Sub(lhs, rhs)
2524 | TermKind::Eq(lhs, rhs)
2525 | TermKind::Lt(lhs, rhs)
2526 | TermKind::Le(lhs, rhs)
2527 | TermKind::Gt(lhs, rhs)
2528 | TermKind::Ge(lhs, rhs)
2529 | TermKind::BvAdd(lhs, rhs)
2530 | TermKind::BvSub(lhs, rhs)
2531 | TermKind::BvMul(lhs, rhs)
2532 | TermKind::BvAnd(lhs, rhs)
2533 | TermKind::BvOr(lhs, rhs)
2534 | TermKind::BvXor(lhs, rhs)
2535 | TermKind::BvUlt(lhs, rhs)
2536 | TermKind::BvUle(lhs, rhs)
2537 | TermKind::BvSlt(lhs, rhs)
2538 | TermKind::BvSle(lhs, rhs) => {
2539 self.track_theory_vars(*lhs, manager);
2540 self.track_theory_vars(*rhs, manager);
2541 }
2542 TermKind::BvUdiv(lhs, rhs)
2545 | TermKind::BvSdiv(lhs, rhs)
2546 | TermKind::BvUrem(lhs, rhs)
2547 | TermKind::BvSrem(lhs, rhs) => {
2548 self.has_bv_arith_ops = true;
2549 self.track_theory_vars(*lhs, manager);
2550 self.track_theory_vars(*rhs, manager);
2551 }
2552 TermKind::Neg(arg) | TermKind::Not(arg) | TermKind::BvNot(arg) => {
2553 self.track_theory_vars(*arg, manager);
2554 }
2555 TermKind::Ite(cond, then_br, else_br) => {
2556 self.track_theory_vars(*cond, manager);
2557 self.track_theory_vars(*then_br, manager);
2558 self.track_theory_vars(*else_br, manager);
2559 }
2560 _ => {}
2562 }
2563 }
2564
2565 fn parse_arith_comparison(
2568 &self,
2569 lhs: TermId,
2570 rhs: TermId,
2571 constraint_type: ArithConstraintType,
2572 reason: TermId,
2573 manager: &TermManager,
2574 ) -> Option<ParsedArithConstraint> {
2575 let mut terms: SmallVec<[(TermId, Rational64); 4]> = SmallVec::new();
2576 let mut constant = Rational64::zero();
2577
2578 self.extract_linear_terms(lhs, Rational64::one(), &mut terms, &mut constant, manager)?;
2580
2581 self.extract_linear_terms(rhs, -Rational64::one(), &mut terms, &mut constant, manager)?;
2584
2585 let mut combined: FxHashMap<TermId, Rational64> = FxHashMap::default();
2587 for (term, coef) in terms {
2588 *combined.entry(term).or_insert(Rational64::zero()) += coef;
2589 }
2590
2591 let final_terms: SmallVec<[(TermId, Rational64); 4]> =
2593 combined.into_iter().filter(|(_, c)| !c.is_zero()).collect();
2594
2595 Some(ParsedArithConstraint {
2596 terms: final_terms,
2597 constant: -constant, constraint_type,
2599 reason_term: reason,
2600 })
2601 }
2602
2603 #[allow(clippy::only_used_in_recursion)]
2606 fn extract_linear_terms(
2607 &self,
2608 term_id: TermId,
2609 scale: Rational64,
2610 terms: &mut SmallVec<[(TermId, Rational64); 4]>,
2611 constant: &mut Rational64,
2612 manager: &TermManager,
2613 ) -> Option<()> {
2614 let term = manager.get(term_id)?;
2615
2616 match &term.kind {
2617 TermKind::IntConst(n) => {
2619 if let Some(val) = n.to_i64() {
2620 *constant += scale * Rational64::from_integer(val);
2621 Some(())
2622 } else {
2623 None
2625 }
2626 }
2627
2628 TermKind::RealConst(r) => {
2630 *constant += scale * *r;
2631 Some(())
2632 }
2633
2634 TermKind::BitVecConst { value, .. } => {
2636 if let Some(val) = value.to_i64() {
2637 *constant += scale * Rational64::from_integer(val);
2638 Some(())
2639 } else {
2640 None
2642 }
2643 }
2644
2645 TermKind::Var(_) => {
2647 terms.push((term_id, scale));
2648 Some(())
2649 }
2650
2651 TermKind::Add(args) => {
2653 for &arg in args {
2654 self.extract_linear_terms(arg, scale, terms, constant, manager)?;
2655 }
2656 Some(())
2657 }
2658
2659 TermKind::Sub(lhs, rhs) => {
2661 self.extract_linear_terms(*lhs, scale, terms, constant, manager)?;
2662 self.extract_linear_terms(*rhs, -scale, terms, constant, manager)?;
2663 Some(())
2664 }
2665
2666 TermKind::Neg(arg) => self.extract_linear_terms(*arg, -scale, terms, constant, manager),
2668
2669 TermKind::Mul(args) => {
2671 let mut const_product = Rational64::one();
2673 let mut var_term: Option<TermId> = None;
2674
2675 for &arg in args {
2676 let arg_term = manager.get(arg)?;
2677 match &arg_term.kind {
2678 TermKind::IntConst(n) => {
2679 if let Some(val) = n.to_i64() {
2680 const_product *= Rational64::from_integer(val);
2681 } else {
2682 return None; }
2684 }
2685 TermKind::RealConst(r) => {
2686 const_product *= *r;
2687 }
2688 _ => {
2689 if var_term.is_some() {
2690 return None;
2692 }
2693 var_term = Some(arg);
2694 }
2695 }
2696 }
2697
2698 let new_scale = scale * const_product;
2699 match var_term {
2700 Some(v) => self.extract_linear_terms(v, new_scale, terms, constant, manager),
2701 None => {
2702 *constant += new_scale;
2704 Some(())
2705 }
2706 }
2707 }
2708
2709 _ => None,
2711 }
2712 }
2713
2714 pub fn assert(&mut self, term: TermId, manager: &mut TermManager) {
2716 let index = self.assertions.len();
2717 self.assertions.push(term);
2718 self.trail.push(TrailOp::AssertionAdded { index });
2719
2720 if let Some(t) = manager.get(term) {
2722 match t.kind {
2723 TermKind::False => {
2724 if !self.has_false_assertion {
2726 self.has_false_assertion = true;
2727 self.trail.push(TrailOp::FalseAssertionSet);
2728 }
2729 if self.produce_unsat_cores {
2730 let na_index = self.named_assertions.len();
2731 self.named_assertions.push(NamedAssertion {
2732 term,
2733 name: None,
2734 index: index as u32,
2735 });
2736 self.trail
2737 .push(TrailOp::NamedAssertionAdded { index: na_index });
2738 }
2739 return;
2740 }
2741 TermKind::True => {
2742 if self.produce_unsat_cores {
2744 let na_index = self.named_assertions.len();
2745 self.named_assertions.push(NamedAssertion {
2746 term,
2747 name: None,
2748 index: index as u32,
2749 });
2750 self.trail
2751 .push(TrailOp::NamedAssertionAdded { index: na_index });
2752 }
2753 return;
2754 }
2755 _ => {}
2756 }
2757 }
2758
2759 let term_to_encode = if self.config.simplify {
2761 self.simplifier.simplify(term, manager)
2762 } else {
2763 term
2764 };
2765
2766 if let Some(t) = manager.get(term_to_encode) {
2768 match t.kind {
2769 TermKind::False => {
2770 if !self.has_false_assertion {
2771 self.has_false_assertion = true;
2772 self.trail.push(TrailOp::FalseAssertionSet);
2773 }
2774 return;
2775 }
2776 TermKind::True => {
2777 return;
2779 }
2780 _ => {}
2781 }
2782 }
2783
2784 if let Some(t) = manager.get(term_to_encode).cloned() {
2787 if let TermKind::Eq(lhs, rhs) = &t.kind {
2788 if let Some((var_term, constructor)) =
2789 self.extract_dt_var_constructor(*lhs, *rhs, manager)
2790 {
2791 if let Some(&existing_con) = self.dt_var_constructors.get(&var_term) {
2792 if existing_con != constructor {
2793 if !self.has_false_assertion {
2795 self.has_false_assertion = true;
2796 self.trail.push(TrailOp::FalseAssertionSet);
2797 }
2798 return;
2799 }
2800 } else {
2801 self.dt_var_constructors.insert(var_term, constructor);
2802 }
2803 }
2804 }
2805 }
2806
2807 if self.polarity_aware {
2809 self.collect_polarities(term_to_encode, Polarity::Positive, manager);
2810 }
2811
2812 let lit = self.encode(term_to_encode, manager);
2814 self.sat.add_clause([lit]);
2815
2816 if self.produce_unsat_cores {
2817 let na_index = self.named_assertions.len();
2818 self.named_assertions.push(NamedAssertion {
2819 term,
2820 name: None,
2821 index: index as u32,
2822 });
2823 self.trail
2824 .push(TrailOp::NamedAssertionAdded { index: na_index });
2825 }
2826 }
2827
2828 pub fn assert_named(&mut self, term: TermId, name: &str, manager: &mut TermManager) {
2830 let index = self.assertions.len();
2831 self.assertions.push(term);
2832 self.trail.push(TrailOp::AssertionAdded { index });
2833
2834 if let Some(t) = manager.get(term) {
2836 match t.kind {
2837 TermKind::False => {
2838 if !self.has_false_assertion {
2840 self.has_false_assertion = true;
2841 self.trail.push(TrailOp::FalseAssertionSet);
2842 }
2843 if self.produce_unsat_cores {
2844 let na_index = self.named_assertions.len();
2845 self.named_assertions.push(NamedAssertion {
2846 term,
2847 name: Some(name.to_string()),
2848 index: index as u32,
2849 });
2850 self.trail
2851 .push(TrailOp::NamedAssertionAdded { index: na_index });
2852 }
2853 return;
2854 }
2855 TermKind::True => {
2856 if self.produce_unsat_cores {
2858 let na_index = self.named_assertions.len();
2859 self.named_assertions.push(NamedAssertion {
2860 term,
2861 name: Some(name.to_string()),
2862 index: index as u32,
2863 });
2864 self.trail
2865 .push(TrailOp::NamedAssertionAdded { index: na_index });
2866 }
2867 return;
2868 }
2869 _ => {}
2870 }
2871 }
2872
2873 if self.polarity_aware {
2875 self.collect_polarities(term, Polarity::Positive, manager);
2876 }
2877
2878 let lit = self.encode(term, manager);
2880 self.sat.add_clause([lit]);
2881
2882 if self.produce_unsat_cores {
2883 let na_index = self.named_assertions.len();
2884 self.named_assertions.push(NamedAssertion {
2885 term,
2886 name: Some(name.to_string()),
2887 index: index as u32,
2888 });
2889 self.trail
2890 .push(TrailOp::NamedAssertionAdded { index: na_index });
2891 }
2892 }
2893
2894 #[must_use]
2896 pub fn get_unsat_core(&self) -> Option<&UnsatCore> {
2897 self.unsat_core.as_ref()
2898 }
2899
2900 fn encode(&mut self, term: TermId, manager: &mut TermManager) -> Lit {
2902 let Some(t) = manager.get(term).cloned() else {
2904 let var = self.get_or_create_var(term);
2905 return Lit::pos(var);
2906 };
2907
2908 match &t.kind {
2909 TermKind::True => {
2910 let var = self.get_or_create_var(manager.mk_true());
2911 self.sat.add_clause([Lit::pos(var)]);
2912 Lit::pos(var)
2913 }
2914 TermKind::False => {
2915 let var = self.get_or_create_var(manager.mk_false());
2916 self.sat.add_clause([Lit::neg(var)]);
2917 Lit::neg(var)
2918 }
2919 TermKind::Var(_) => {
2920 let var = self.get_or_create_var(term);
2921 let is_int = t.sort == manager.sorts.int_sort;
2923 let is_real = t.sort == manager.sorts.real_sort;
2924
2925 if is_int || is_real {
2926 if !self.arith_terms.contains(&term) {
2928 self.arith_terms.insert(term);
2929 self.trail.push(TrailOp::ArithTermAdded { term });
2930 self.arith.intern(term);
2932 }
2933 } else if let Some(sort) = manager.sorts.get(t.sort)
2934 && sort.is_bitvec()
2935 && !self.bv_terms.contains(&term)
2936 {
2937 self.bv_terms.insert(term);
2938 self.trail.push(TrailOp::BvTermAdded { term });
2939 if let Some(width) = sort.bitvec_width() {
2941 self.bv.new_bv(term, width);
2942 }
2943 }
2944 Lit::pos(var)
2945 }
2946 TermKind::Not(arg) => {
2947 let arg_lit = self.encode(*arg, manager);
2948 arg_lit.negate()
2949 }
2950 TermKind::And(args) => {
2951 let result_var = self.get_or_create_var(term);
2952 let result = Lit::pos(result_var);
2953
2954 let mut arg_lits: Vec<Lit> = Vec::new();
2955 for &arg in args {
2956 arg_lits.push(self.encode(arg, manager));
2957 }
2958
2959 let polarity = if self.polarity_aware {
2961 self.polarities
2962 .get(&term)
2963 .copied()
2964 .unwrap_or(Polarity::Both)
2965 } else {
2966 Polarity::Both
2967 };
2968
2969 if polarity != Polarity::Negative {
2972 for &arg in &arg_lits {
2973 self.sat.add_clause([result.negate(), arg]);
2974 }
2975 }
2976
2977 if polarity != Polarity::Positive {
2980 let mut clause: Vec<Lit> = arg_lits.iter().map(|l| l.negate()).collect();
2981 clause.push(result);
2982 self.sat.add_clause(clause);
2983 }
2984
2985 result
2986 }
2987 TermKind::Or(args) => {
2988 let result_var = self.get_or_create_var(term);
2989 let result = Lit::pos(result_var);
2990
2991 let mut arg_lits: Vec<Lit> = Vec::new();
2992 for &arg in args {
2993 arg_lits.push(self.encode(arg, manager));
2994 }
2995
2996 let polarity = if self.polarity_aware {
2998 self.polarities
2999 .get(&term)
3000 .copied()
3001 .unwrap_or(Polarity::Both)
3002 } else {
3003 Polarity::Both
3004 };
3005
3006 if polarity != Polarity::Negative {
3009 let mut clause: Vec<Lit> = vec![result.negate()];
3010 clause.extend(arg_lits.iter().copied());
3011 self.sat.add_clause(clause);
3012 }
3013
3014 if polarity != Polarity::Positive {
3017 for &arg in &arg_lits {
3018 self.sat.add_clause([arg.negate(), result]);
3019 }
3020 }
3021
3022 result
3023 }
3024 TermKind::Xor(lhs, rhs) => {
3025 let lhs_lit = self.encode(*lhs, manager);
3026 let rhs_lit = self.encode(*rhs, manager);
3027
3028 let result_var = self.get_or_create_var(term);
3029 let result = Lit::pos(result_var);
3030
3031 self.sat.add_clause([result.negate(), lhs_lit, rhs_lit]);
3036 self.sat
3038 .add_clause([result.negate(), lhs_lit.negate(), rhs_lit.negate()]);
3039
3040 self.sat.add_clause([lhs_lit.negate(), rhs_lit, result]);
3042 self.sat.add_clause([lhs_lit, rhs_lit.negate(), result]);
3044
3045 result
3046 }
3047 TermKind::Implies(lhs, rhs) => {
3048 let lhs_lit = self.encode(*lhs, manager);
3049 let rhs_lit = self.encode(*rhs, manager);
3050
3051 let result_var = self.get_or_create_var(term);
3052 let result = Lit::pos(result_var);
3053
3054 self.sat
3057 .add_clause([result.negate(), lhs_lit.negate(), rhs_lit]);
3058
3059 self.sat.add_clause([lhs_lit, result]);
3062 self.sat.add_clause([rhs_lit.negate(), result]);
3063
3064 result
3065 }
3066 TermKind::Ite(cond, then_br, else_br) => {
3067 let cond_lit = self.encode(*cond, manager);
3068 let then_lit = self.encode(*then_br, manager);
3069 let else_lit = self.encode(*else_br, manager);
3070
3071 let result_var = self.get_or_create_var(term);
3072 let result = Lit::pos(result_var);
3073
3074 self.sat
3077 .add_clause([cond_lit.negate(), result.negate(), then_lit]);
3078 self.sat
3080 .add_clause([cond_lit.negate(), then_lit.negate(), result]);
3081
3082 self.sat.add_clause([cond_lit, result.negate(), else_lit]);
3084 self.sat.add_clause([cond_lit, else_lit.negate(), result]);
3086
3087 result
3088 }
3089 TermKind::Eq(lhs, rhs) => {
3090 let lhs_term = manager.get(*lhs);
3092 let is_bool_eq = lhs_term.is_some_and(|t| t.sort == manager.sorts.bool_sort);
3093
3094 if is_bool_eq {
3095 let lhs_lit = self.encode(*lhs, manager);
3097 let rhs_lit = self.encode(*rhs, manager);
3098
3099 let result_var = self.get_or_create_var(term);
3100 let result = Lit::pos(result_var);
3101
3102 self.sat
3105 .add_clause([result.negate(), lhs_lit.negate(), rhs_lit]);
3106 self.sat
3107 .add_clause([result.negate(), rhs_lit.negate(), lhs_lit]);
3108
3109 self.sat.add_clause([lhs_lit, rhs_lit, result]);
3111 self.sat
3112 .add_clause([lhs_lit.negate(), rhs_lit.negate(), result]);
3113
3114 result
3115 } else {
3116 let var = self.get_or_create_var(term);
3119 self.var_to_constraint
3120 .insert(var, Constraint::Eq(*lhs, *rhs));
3121 self.trail.push(TrailOp::ConstraintAdded { var });
3122
3123 self.track_theory_vars(*lhs, manager);
3125 self.track_theory_vars(*rhs, manager);
3126
3127 let is_arith = lhs_term.is_some_and(|t| {
3130 t.sort == manager.sorts.int_sort || t.sort == manager.sorts.real_sort
3131 });
3132 if is_arith {
3133 if let Some(parsed) = self.parse_arith_comparison(
3136 *lhs,
3137 *rhs,
3138 ArithConstraintType::Le,
3139 term,
3140 manager,
3141 ) {
3142 self.var_to_parsed_arith.insert(var, parsed);
3143 }
3144 }
3145
3146 Lit::pos(var)
3147 }
3148 }
3149 TermKind::Distinct(args) => {
3150 if args.len() <= 1 {
3153 let var = self.get_or_create_var(manager.mk_true());
3155 return Lit::pos(var);
3156 }
3157
3158 let result_var = self.get_or_create_var(term);
3159 let result = Lit::pos(result_var);
3160
3161 let mut diseq_lits = Vec::new();
3162 for i in 0..args.len() {
3163 for j in (i + 1)..args.len() {
3164 let eq = manager.mk_eq(args[i], args[j]);
3165 let eq_lit = self.encode(eq, manager);
3166 diseq_lits.push(eq_lit.negate());
3167 }
3168 }
3169
3170 for &diseq in &diseq_lits {
3172 self.sat.add_clause([result.negate(), diseq]);
3173 }
3174
3175 let mut clause: Vec<Lit> = diseq_lits.iter().map(|l| l.negate()).collect();
3177 clause.push(result);
3178 self.sat.add_clause(clause);
3179
3180 result
3181 }
3182 TermKind::Let { bindings, body } => {
3183 let substituted = *body;
3187 for (name, value) in bindings.iter().rev() {
3188 let _ = (name, value);
3191 }
3192 self.encode(substituted, manager)
3193 }
3194 TermKind::IntConst(_) | TermKind::RealConst(_) | TermKind::BitVecConst { .. } => {
3197 let var = self.get_or_create_var(term);
3200 Lit::pos(var)
3201 }
3202 TermKind::Neg(_)
3203 | TermKind::Add(_)
3204 | TermKind::Sub(_, _)
3205 | TermKind::Mul(_)
3206 | TermKind::Div(_, _)
3207 | TermKind::Mod(_, _) => {
3208 let var = self.get_or_create_var(term);
3210 Lit::pos(var)
3211 }
3212 TermKind::Lt(lhs, rhs) => {
3213 let var = self.get_or_create_var(term);
3215 self.var_to_constraint
3216 .insert(var, Constraint::Lt(*lhs, *rhs));
3217 self.trail.push(TrailOp::ConstraintAdded { var });
3218 if let Some(parsed) =
3220 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Lt, term, manager)
3221 {
3222 self.var_to_parsed_arith.insert(var, parsed);
3223 }
3224 self.track_theory_vars(*lhs, manager);
3226 self.track_theory_vars(*rhs, manager);
3227 Lit::pos(var)
3228 }
3229 TermKind::Le(lhs, rhs) => {
3230 let var = self.get_or_create_var(term);
3232 self.var_to_constraint
3233 .insert(var, Constraint::Le(*lhs, *rhs));
3234 self.trail.push(TrailOp::ConstraintAdded { var });
3235 if let Some(parsed) =
3237 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Le, term, manager)
3238 {
3239 self.var_to_parsed_arith.insert(var, parsed);
3240 }
3241 self.track_theory_vars(*lhs, manager);
3243 self.track_theory_vars(*rhs, manager);
3244 Lit::pos(var)
3245 }
3246 TermKind::Gt(lhs, rhs) => {
3247 let var = self.get_or_create_var(term);
3249 self.var_to_constraint
3250 .insert(var, Constraint::Gt(*lhs, *rhs));
3251 self.trail.push(TrailOp::ConstraintAdded { var });
3252 if let Some(parsed) =
3254 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Gt, term, manager)
3255 {
3256 self.var_to_parsed_arith.insert(var, parsed);
3257 }
3258 self.track_theory_vars(*lhs, manager);
3260 self.track_theory_vars(*rhs, manager);
3261 Lit::pos(var)
3262 }
3263 TermKind::Ge(lhs, rhs) => {
3264 let var = self.get_or_create_var(term);
3266 self.var_to_constraint
3267 .insert(var, Constraint::Ge(*lhs, *rhs));
3268 self.trail.push(TrailOp::ConstraintAdded { var });
3269 if let Some(parsed) =
3271 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Ge, term, manager)
3272 {
3273 self.var_to_parsed_arith.insert(var, parsed);
3274 }
3275 self.track_theory_vars(*lhs, manager);
3277 self.track_theory_vars(*rhs, manager);
3278 Lit::pos(var)
3279 }
3280 TermKind::BvConcat(_, _)
3281 | TermKind::BvExtract { .. }
3282 | TermKind::BvNot(_)
3283 | TermKind::BvAnd(_, _)
3284 | TermKind::BvOr(_, _)
3285 | TermKind::BvXor(_, _)
3286 | TermKind::BvAdd(_, _)
3287 | TermKind::BvSub(_, _)
3288 | TermKind::BvMul(_, _)
3289 | TermKind::BvShl(_, _)
3290 | TermKind::BvLshr(_, _)
3291 | TermKind::BvAshr(_, _) => {
3292 let var = self.get_or_create_var(term);
3294 Lit::pos(var)
3295 }
3296 TermKind::BvUdiv(_, _)
3297 | TermKind::BvSdiv(_, _)
3298 | TermKind::BvUrem(_, _)
3299 | TermKind::BvSrem(_, _) => {
3300 self.has_bv_arith_ops = true;
3303 let var = self.get_or_create_var(term);
3304 Lit::pos(var)
3305 }
3306 TermKind::BvUlt(lhs, rhs) => {
3307 let var = self.get_or_create_var(term);
3309 self.var_to_constraint
3310 .insert(var, Constraint::Lt(*lhs, *rhs));
3311 self.trail.push(TrailOp::ConstraintAdded { var });
3312 if let Some(parsed) =
3314 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Lt, term, manager)
3315 {
3316 self.var_to_parsed_arith.insert(var, parsed);
3317 }
3318 self.track_theory_vars(*lhs, manager);
3320 self.track_theory_vars(*rhs, manager);
3321 Lit::pos(var)
3322 }
3323 TermKind::BvUle(lhs, rhs) => {
3324 let var = self.get_or_create_var(term);
3326 self.var_to_constraint
3327 .insert(var, Constraint::Le(*lhs, *rhs));
3328 self.trail.push(TrailOp::ConstraintAdded { var });
3329 if let Some(parsed) =
3330 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Le, term, manager)
3331 {
3332 self.var_to_parsed_arith.insert(var, parsed);
3333 }
3334 self.track_theory_vars(*lhs, manager);
3336 self.track_theory_vars(*rhs, manager);
3337 Lit::pos(var)
3338 }
3339 TermKind::BvSlt(lhs, rhs) => {
3340 let var = self.get_or_create_var(term);
3342 self.var_to_constraint
3343 .insert(var, Constraint::Lt(*lhs, *rhs));
3344 self.trail.push(TrailOp::ConstraintAdded { var });
3345 if let Some(parsed) =
3346 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Lt, term, manager)
3347 {
3348 self.var_to_parsed_arith.insert(var, parsed);
3349 }
3350 self.track_theory_vars(*lhs, manager);
3352 self.track_theory_vars(*rhs, manager);
3353 Lit::pos(var)
3354 }
3355 TermKind::BvSle(lhs, rhs) => {
3356 let var = self.get_or_create_var(term);
3358 self.var_to_constraint
3359 .insert(var, Constraint::Le(*lhs, *rhs));
3360 self.trail.push(TrailOp::ConstraintAdded { var });
3361 if let Some(parsed) =
3362 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Le, term, manager)
3363 {
3364 self.var_to_parsed_arith.insert(var, parsed);
3365 }
3366 self.track_theory_vars(*lhs, manager);
3368 self.track_theory_vars(*rhs, manager);
3369 Lit::pos(var)
3370 }
3371 TermKind::Select(_, _) | TermKind::Store(_, _, _) => {
3372 let var = self.get_or_create_var(term);
3374 Lit::pos(var)
3375 }
3376 TermKind::Apply { .. } => {
3377 let var = self.get_or_create_var(term);
3379 Lit::pos(var)
3380 }
3381 TermKind::Forall { patterns, .. } => {
3382 self.has_quantifiers = true;
3384 self.mbqi.add_quantifier(term, manager);
3385 for pattern in patterns {
3387 for &trigger in pattern {
3388 self.mbqi.collect_ground_terms(trigger, manager);
3389 }
3390 }
3391 let var = self.get_or_create_var(term);
3393 Lit::pos(var)
3394 }
3395 TermKind::Exists { patterns, .. } => {
3396 self.has_quantifiers = true;
3398 self.mbqi.add_quantifier(term, manager);
3399 for pattern in patterns {
3401 for &trigger in pattern {
3402 self.mbqi.collect_ground_terms(trigger, manager);
3403 }
3404 }
3405 let var = self.get_or_create_var(term);
3407 Lit::pos(var)
3408 }
3409 TermKind::StringLit(_)
3411 | TermKind::StrConcat(_, _)
3412 | TermKind::StrLen(_)
3413 | TermKind::StrSubstr(_, _, _)
3414 | TermKind::StrAt(_, _)
3415 | TermKind::StrReplace(_, _, _)
3416 | TermKind::StrReplaceAll(_, _, _)
3417 | TermKind::StrToInt(_)
3418 | TermKind::IntToStr(_)
3419 | TermKind::StrInRe(_, _) => {
3420 let var = self.get_or_create_var(term);
3422 Lit::pos(var)
3423 }
3424 TermKind::StrContains(_, _)
3425 | TermKind::StrPrefixOf(_, _)
3426 | TermKind::StrSuffixOf(_, _)
3427 | TermKind::StrIndexOf(_, _, _) => {
3428 let var = self.get_or_create_var(term);
3430 Lit::pos(var)
3431 }
3432 TermKind::FpLit { .. }
3434 | TermKind::FpPlusInfinity { .. }
3435 | TermKind::FpMinusInfinity { .. }
3436 | TermKind::FpPlusZero { .. }
3437 | TermKind::FpMinusZero { .. }
3438 | TermKind::FpNaN { .. } => {
3439 let var = self.get_or_create_var(term);
3441 Lit::pos(var)
3442 }
3443 TermKind::FpAbs(_)
3445 | TermKind::FpNeg(_)
3446 | TermKind::FpSqrt(_, _)
3447 | TermKind::FpRoundToIntegral(_, _)
3448 | TermKind::FpAdd(_, _, _)
3449 | TermKind::FpSub(_, _, _)
3450 | TermKind::FpMul(_, _, _)
3451 | TermKind::FpDiv(_, _, _)
3452 | TermKind::FpRem(_, _)
3453 | TermKind::FpMin(_, _)
3454 | TermKind::FpMax(_, _)
3455 | TermKind::FpFma(_, _, _, _) => {
3456 let var = self.get_or_create_var(term);
3458 Lit::pos(var)
3459 }
3460 TermKind::FpLeq(_, _)
3462 | TermKind::FpLt(_, _)
3463 | TermKind::FpGeq(_, _)
3464 | TermKind::FpGt(_, _)
3465 | TermKind::FpEq(_, _)
3466 | TermKind::FpIsNormal(_)
3467 | TermKind::FpIsSubnormal(_)
3468 | TermKind::FpIsZero(_)
3469 | TermKind::FpIsInfinite(_)
3470 | TermKind::FpIsNaN(_)
3471 | TermKind::FpIsNegative(_)
3472 | TermKind::FpIsPositive(_) => {
3473 let var = self.get_or_create_var(term);
3475 Lit::pos(var)
3476 }
3477 TermKind::FpToFp { .. }
3479 | TermKind::FpToSBV { .. }
3480 | TermKind::FpToUBV { .. }
3481 | TermKind::FpToReal(_)
3482 | TermKind::RealToFp { .. }
3483 | TermKind::SBVToFp { .. }
3484 | TermKind::UBVToFp { .. } => {
3485 let var = self.get_or_create_var(term);
3487 Lit::pos(var)
3488 }
3489 TermKind::DtConstructor { .. }
3491 | TermKind::DtTester { .. }
3492 | TermKind::DtSelector { .. } => {
3493 let var = self.get_or_create_var(term);
3495 Lit::pos(var)
3496 }
3497 TermKind::Match { .. } => {
3499 let var = self.get_or_create_var(term);
3501 Lit::pos(var)
3502 }
3503 }
3504 }
3505
3506 pub fn check(&mut self, manager: &mut TermManager) -> SolverResult {
3508 if self.has_false_assertion {
3510 self.build_unsat_core_trivial_false();
3511 return SolverResult::Unsat;
3512 }
3513
3514 if self.assertions.is_empty() {
3515 return SolverResult::Sat;
3516 }
3517
3518 if self.check_string_constraints(manager) {
3520 return SolverResult::Unsat;
3521 }
3522
3523 if self.check_fp_constraints(manager) {
3525 return SolverResult::Unsat;
3526 }
3527
3528 if self.check_dt_constraints(manager) {
3530 return SolverResult::Unsat;
3531 }
3532
3533 if self.check_array_constraints(manager) {
3535 return SolverResult::Unsat;
3536 }
3537
3538 if self.check_bv_constraints(manager) {
3540 return SolverResult::Unsat;
3541 }
3542
3543 if self.config.max_conflicts > 0 && self.statistics.conflicts >= self.config.max_conflicts {
3545 return SolverResult::Unknown;
3546 }
3547 if self.config.max_decisions > 0 && self.statistics.decisions >= self.config.max_decisions {
3548 return SolverResult::Unknown;
3549 }
3550
3551 let mut theory_manager = TheoryManager::new(
3553 manager,
3554 &mut self.euf,
3555 &mut self.arith,
3556 &mut self.bv,
3557 &self.bv_terms,
3558 &self.var_to_constraint,
3559 &self.var_to_parsed_arith,
3560 &self.term_to_var,
3561 self.config.theory_mode,
3562 &mut self.statistics,
3563 self.config.max_conflicts,
3564 self.config.max_decisions,
3565 self.has_bv_arith_ops,
3566 );
3567
3568 let max_mbqi_iterations = 100;
3570 let mut mbqi_iteration = 0;
3571
3572 loop {
3573 let sat_result = self.sat.solve_with_theory(&mut theory_manager);
3574
3575 match sat_result {
3576 SatResult::Unsat => {
3577 self.build_unsat_core();
3578 return SolverResult::Unsat;
3579 }
3580 SatResult::Unknown => {
3581 return SolverResult::Unknown;
3582 }
3583 SatResult::Sat => {
3584 if !self.has_quantifiers {
3586 self.build_model(manager);
3587 self.unsat_core = None;
3588 return SolverResult::Sat;
3589 }
3590
3591 self.build_model(manager);
3593
3594 let model_assignments = self
3596 .model
3597 .as_ref()
3598 .map(|m| m.assignments().clone())
3599 .unwrap_or_default();
3600 let mbqi_result = self.mbqi.check_with_model(&model_assignments, manager);
3601
3602 match mbqi_result {
3603 MBQIResult::NoQuantifiers | MBQIResult::Satisfied => {
3604 self.unsat_core = None;
3606 return SolverResult::Sat;
3607 }
3608 MBQIResult::InstantiationLimit => {
3609 return SolverResult::Unknown;
3611 }
3612 MBQIResult::Conflict {
3613 quantifier: _,
3614 reason,
3615 } => {
3616 let lits: Vec<Lit> = reason
3618 .iter()
3619 .filter_map(|&t| self.term_to_var.get(&t).map(|&v| Lit::neg(v)))
3620 .collect();
3621 if !lits.is_empty() {
3622 self.sat.add_clause(lits);
3623 }
3624 }
3626 MBQIResult::NewInstantiations(instantiations) => {
3627 for inst in instantiations {
3629 let lit = self.encode(inst.result, manager);
3632 self.sat.add_clause([lit]);
3633 }
3634 }
3636 MBQIResult::Unknown => {
3637 return SolverResult::Unknown;
3639 }
3640 }
3641
3642 mbqi_iteration += 1;
3643 if mbqi_iteration >= max_mbqi_iterations {
3644 return SolverResult::Unknown;
3645 }
3646
3647 theory_manager = TheoryManager::new(
3649 manager,
3650 &mut self.euf,
3651 &mut self.arith,
3652 &mut self.bv,
3653 &self.bv_terms,
3654 &self.var_to_constraint,
3655 &self.var_to_parsed_arith,
3656 &self.term_to_var,
3657 self.config.theory_mode,
3658 &mut self.statistics,
3659 self.config.max_conflicts,
3660 self.config.max_decisions,
3661 self.has_bv_arith_ops,
3662 );
3663 }
3664 }
3665 }
3666 }
3667
3668 fn check_string_constraints(&self, manager: &TermManager) -> bool {
3671 let mut string_assignments: FxHashMap<TermId, String> = FxHashMap::default();
3673 let mut length_constraints: FxHashMap<TermId, i64> = FxHashMap::default();
3674 let mut concat_equalities: Vec<(Vec<TermId>, String)> = Vec::new();
3675 let mut replace_all_constraints: Vec<(TermId, TermId, String, String, String)> = Vec::new();
3676
3677 for &assertion in &self.assertions {
3679 self.collect_string_constraints(
3680 assertion,
3681 manager,
3682 &mut string_assignments,
3683 &mut length_constraints,
3684 &mut concat_equalities,
3685 &mut replace_all_constraints,
3686 );
3687 }
3688
3689 for &assertion in &self.assertions {
3692 self.collect_replace_all_with_resolved_vars(
3693 assertion,
3694 manager,
3695 &string_assignments,
3696 &mut replace_all_constraints,
3697 );
3698 }
3699
3700 for (&var, &declared_len) in &length_constraints {
3703 if let Some(value) = string_assignments.get(&var) {
3704 let actual_len = value.chars().count() as i64;
3705 if actual_len != declared_len {
3706 return true; }
3708 }
3709 }
3710
3711 for (operands, result_str) in &concat_equalities {
3714 let result_len = result_str.chars().count() as i64;
3715 let mut total_declared_len = 0i64;
3716 let mut all_have_length = true;
3717
3718 for operand in operands {
3719 if let Some(&len) = length_constraints.get(operand) {
3720 total_declared_len += len;
3721 } else if let Some(value) = string_assignments.get(operand) {
3722 total_declared_len += value.chars().count() as i64;
3723 } else {
3724 all_have_length = false;
3725 break;
3726 }
3727 }
3728
3729 if all_have_length && total_declared_len != result_len {
3730 return true; }
3732 }
3733
3734 for (result_var, source_var, source_val, pattern, replacement) in &replace_all_constraints {
3738 if let Some(result_val) = string_assignments.get(result_var) {
3740 if !pattern.is_empty() && source_val.contains(pattern) && pattern != replacement {
3743 let actual_result = source_val.replace(pattern, replacement);
3745 if &actual_result != result_val {
3746 return true; }
3748 }
3749 }
3750 if length_constraints.contains_key(source_var) {
3753 if let Some(result_val) = string_assignments.get(result_var) {
3754 if !pattern.is_empty() {
3756 if source_val.contains(pattern) && pattern != replacement {
3758 if source_val == result_val.as_str() {
3760 return true; }
3762 }
3763 }
3764 }
3765 }
3766 }
3767
3768 false }
3770
3771 fn collect_string_constraints(
3773 &self,
3774 term: TermId,
3775 manager: &TermManager,
3776 string_assignments: &mut FxHashMap<TermId, String>,
3777 length_constraints: &mut FxHashMap<TermId, i64>,
3778 concat_equalities: &mut Vec<(Vec<TermId>, String)>,
3779 replace_all_constraints: &mut Vec<(TermId, TermId, String, String, String)>,
3780 ) {
3781 let Some(term_data) = manager.get(term) else {
3782 return;
3783 };
3784
3785 match &term_data.kind {
3786 TermKind::Eq(lhs, rhs) => {
3788 if let Some(lit) = self.get_string_literal(*rhs, manager) {
3790 if self.is_string_variable(*lhs, manager) {
3792 string_assignments.insert(*lhs, lit);
3793 }
3794 } else if let Some(lit) = self.get_string_literal(*lhs, manager) {
3795 if self.is_string_variable(*rhs, manager) {
3797 string_assignments.insert(*rhs, lit);
3798 }
3799 }
3800
3801 if let Some((var, len)) = self.extract_length_constraint(*lhs, *rhs, manager) {
3803 length_constraints.insert(var, len);
3804 } else if let Some((var, len)) = self.extract_length_constraint(*rhs, *lhs, manager)
3805 {
3806 length_constraints.insert(var, len);
3807 }
3808
3809 if let Some(result_str) = self.get_string_literal(*rhs, manager) {
3811 if let Some(operands) = self.extract_concat_operands(*lhs, manager) {
3812 concat_equalities.push((operands, result_str));
3813 }
3814 } else if let Some(result_str) = self.get_string_literal(*lhs, manager) {
3815 if let Some(operands) = self.extract_concat_operands(*rhs, manager) {
3816 concat_equalities.push((operands, result_str));
3817 }
3818 }
3819
3820 if let Some((source, pattern, replacement)) =
3822 self.extract_replace_all(*rhs, manager)
3823 {
3824 let source_val = self
3826 .get_string_literal(source, manager)
3827 .or_else(|| string_assignments.get(&source).cloned());
3828 if let Some(source_val) = source_val {
3829 if let Some(pattern_val) = self.get_string_literal(pattern, manager) {
3830 if let Some(replacement_val) =
3831 self.get_string_literal(replacement, manager)
3832 {
3833 replace_all_constraints.push((
3834 *lhs,
3835 source,
3836 source_val,
3837 pattern_val,
3838 replacement_val,
3839 ));
3840 }
3841 }
3842 }
3843 } else if let Some((source, pattern, replacement)) =
3844 self.extract_replace_all(*lhs, manager)
3845 {
3846 let source_val = self
3848 .get_string_literal(source, manager)
3849 .or_else(|| string_assignments.get(&source).cloned());
3850 if let Some(source_val) = source_val {
3851 if let Some(pattern_val) = self.get_string_literal(pattern, manager) {
3852 if let Some(replacement_val) =
3853 self.get_string_literal(replacement, manager)
3854 {
3855 replace_all_constraints.push((
3856 *rhs,
3857 source,
3858 source_val,
3859 pattern_val,
3860 replacement_val,
3861 ));
3862 }
3863 }
3864 }
3865 }
3866
3867 self.collect_string_constraints(
3869 *lhs,
3870 manager,
3871 string_assignments,
3872 length_constraints,
3873 concat_equalities,
3874 replace_all_constraints,
3875 );
3876 self.collect_string_constraints(
3877 *rhs,
3878 manager,
3879 string_assignments,
3880 length_constraints,
3881 concat_equalities,
3882 replace_all_constraints,
3883 );
3884 }
3885
3886 TermKind::And(args) => {
3888 for &arg in args {
3889 self.collect_string_constraints(
3890 arg,
3891 manager,
3892 string_assignments,
3893 length_constraints,
3894 concat_equalities,
3895 replace_all_constraints,
3896 );
3897 }
3898 }
3899
3900 TermKind::Or(args) => {
3902 for &arg in args {
3903 self.collect_string_constraints(
3904 arg,
3905 manager,
3906 string_assignments,
3907 length_constraints,
3908 concat_equalities,
3909 replace_all_constraints,
3910 );
3911 }
3912 }
3913
3914 TermKind::Not(inner) => {
3915 self.collect_string_constraints(
3916 *inner,
3917 manager,
3918 string_assignments,
3919 length_constraints,
3920 concat_equalities,
3921 replace_all_constraints,
3922 );
3923 }
3924
3925 TermKind::Implies(lhs, rhs) => {
3926 self.collect_string_constraints(
3927 *lhs,
3928 manager,
3929 string_assignments,
3930 length_constraints,
3931 concat_equalities,
3932 replace_all_constraints,
3933 );
3934 self.collect_string_constraints(
3935 *rhs,
3936 manager,
3937 string_assignments,
3938 length_constraints,
3939 concat_equalities,
3940 replace_all_constraints,
3941 );
3942 }
3943
3944 _ => {}
3945 }
3946 }
3947
3948 fn get_string_literal(&self, term: TermId, manager: &TermManager) -> Option<String> {
3950 let term_data = manager.get(term)?;
3951 if let TermKind::StringLit(s) = &term_data.kind {
3952 Some(s.clone())
3953 } else {
3954 None
3955 }
3956 }
3957
3958 fn is_string_variable(&self, term: TermId, manager: &TermManager) -> bool {
3960 let Some(term_data) = manager.get(term) else {
3961 return false;
3962 };
3963 matches!(term_data.kind, TermKind::Var(_))
3964 }
3965
3966 fn extract_length_constraint(
3968 &self,
3969 lhs: TermId,
3970 rhs: TermId,
3971 manager: &TermManager,
3972 ) -> Option<(TermId, i64)> {
3973 let lhs_data = manager.get(lhs)?;
3974 let rhs_data = manager.get(rhs)?;
3975
3976 if let TermKind::StrLen(inner) = &lhs_data.kind {
3978 if let TermKind::IntConst(n) = &rhs_data.kind {
3979 return n.to_i64().map(|len| (*inner, len));
3980 }
3981 }
3982
3983 None
3984 }
3985
3986 fn extract_concat_operands(&self, term: TermId, manager: &TermManager) -> Option<Vec<TermId>> {
3988 let term_data = manager.get(term)?;
3989
3990 match &term_data.kind {
3991 TermKind::StrConcat(lhs, rhs) => {
3992 let mut operands = Vec::new();
3993 self.flatten_concat(*lhs, manager, &mut operands);
3995 self.flatten_concat(*rhs, manager, &mut operands);
3996 Some(operands)
3997 }
3998 _ => None,
3999 }
4000 }
4001
4002 fn flatten_concat(&self, term: TermId, manager: &TermManager, operands: &mut Vec<TermId>) {
4004 let Some(term_data) = manager.get(term) else {
4005 operands.push(term);
4006 return;
4007 };
4008
4009 match &term_data.kind {
4010 TermKind::StrConcat(lhs, rhs) => {
4011 self.flatten_concat(*lhs, manager, operands);
4012 self.flatten_concat(*rhs, manager, operands);
4013 }
4014 _ => {
4015 operands.push(term);
4016 }
4017 }
4018 }
4019
4020 fn extract_replace_all(
4022 &self,
4023 term: TermId,
4024 manager: &TermManager,
4025 ) -> Option<(TermId, TermId, TermId)> {
4026 let term_data = manager.get(term)?;
4027 if let TermKind::StrReplaceAll(source, pattern, replacement) = &term_data.kind {
4028 Some((*source, *pattern, *replacement))
4029 } else {
4030 None
4031 }
4032 }
4033
4034 fn collect_replace_all_with_resolved_vars(
4036 &self,
4037 term: TermId,
4038 manager: &TermManager,
4039 string_assignments: &FxHashMap<TermId, String>,
4040 replace_all_constraints: &mut Vec<(TermId, TermId, String, String, String)>,
4041 ) {
4042 let Some(term_data) = manager.get(term) else {
4043 return;
4044 };
4045
4046 match &term_data.kind {
4047 TermKind::Eq(lhs, rhs) => {
4048 if let Some((source, pattern, replacement)) =
4050 self.extract_replace_all(*rhs, manager)
4051 {
4052 if let Some(source_val) = string_assignments.get(&source) {
4054 if let Some(pattern_val) = self.get_string_literal(pattern, manager) {
4055 if let Some(replacement_val) =
4056 self.get_string_literal(replacement, manager)
4057 {
4058 let entry = (
4060 *lhs,
4061 source,
4062 source_val.clone(),
4063 pattern_val,
4064 replacement_val,
4065 );
4066 if !replace_all_constraints.contains(&entry) {
4067 replace_all_constraints.push(entry);
4068 }
4069 }
4070 }
4071 }
4072 } else if let Some((source, pattern, replacement)) =
4073 self.extract_replace_all(*lhs, manager)
4074 {
4075 if let Some(source_val) = string_assignments.get(&source) {
4077 if let Some(pattern_val) = self.get_string_literal(pattern, manager) {
4078 if let Some(replacement_val) =
4079 self.get_string_literal(replacement, manager)
4080 {
4081 let entry = (
4083 *rhs,
4084 source,
4085 source_val.clone(),
4086 pattern_val,
4087 replacement_val,
4088 );
4089 if !replace_all_constraints.contains(&entry) {
4090 replace_all_constraints.push(entry);
4091 }
4092 }
4093 }
4094 }
4095 }
4096
4097 self.collect_replace_all_with_resolved_vars(
4099 *lhs,
4100 manager,
4101 string_assignments,
4102 replace_all_constraints,
4103 );
4104 self.collect_replace_all_with_resolved_vars(
4105 *rhs,
4106 manager,
4107 string_assignments,
4108 replace_all_constraints,
4109 );
4110 }
4111 TermKind::And(args) => {
4112 for &arg in args {
4113 self.collect_replace_all_with_resolved_vars(
4114 arg,
4115 manager,
4116 string_assignments,
4117 replace_all_constraints,
4118 );
4119 }
4120 }
4121 TermKind::Or(args) => {
4122 for &arg in args {
4123 self.collect_replace_all_with_resolved_vars(
4124 arg,
4125 manager,
4126 string_assignments,
4127 replace_all_constraints,
4128 );
4129 }
4130 }
4131 TermKind::Not(inner) => {
4132 self.collect_replace_all_with_resolved_vars(
4133 *inner,
4134 manager,
4135 string_assignments,
4136 replace_all_constraints,
4137 );
4138 }
4139 _ => {}
4140 }
4141 }
4142
4143 fn check_fp_constraints(&self, manager: &TermManager) -> bool {
4146 let mut fp_additions: Vec<(TermId, TermId, TermId, TermId, RoundingMode)> = Vec::new();
4148 let mut fp_divisions: Vec<(TermId, TermId, TermId, TermId, RoundingMode)> = Vec::new();
4149 let mut fp_multiplications: Vec<(TermId, TermId, TermId, TermId, RoundingMode)> =
4150 Vec::new();
4151 let mut fp_comparisons: Vec<(TermId, TermId, bool)> = Vec::new(); let mut fp_equalities: Vec<(TermId, TermId)> = Vec::new();
4153 let mut fp_literals: FxHashMap<TermId, f64> = FxHashMap::default();
4154 let mut rounding_add_results: FxHashMap<(TermId, TermId, RoundingMode), TermId> =
4155 FxHashMap::default();
4156
4157 let mut fp_is_zero: FxHashSet<TermId> = FxHashSet::default();
4159 let mut fp_is_positive: FxHashSet<TermId> = FxHashSet::default();
4160 let mut fp_is_negative: FxHashSet<TermId> = FxHashSet::default();
4161 let mut fp_not_nan: FxHashSet<TermId> = FxHashSet::default();
4162 let mut fp_gt_comparisons: Vec<(TermId, TermId)> = Vec::new(); let mut fp_lt_comparisons: Vec<(TermId, TermId)> = Vec::new(); let mut fp_conversions: Vec<(TermId, u32, u32, TermId)> = Vec::new(); let mut real_to_fp_conversions: Vec<(TermId, u32, u32, TermId)> = Vec::new(); let mut fp_subtractions: Vec<(TermId, TermId, TermId)> = Vec::new(); for &assertion in &self.assertions {
4170 self.collect_fp_constraints_extended(
4171 assertion,
4172 manager,
4173 &mut fp_additions,
4174 &mut fp_divisions,
4175 &mut fp_multiplications,
4176 &mut fp_comparisons,
4177 &mut fp_equalities,
4178 &mut fp_literals,
4179 &mut rounding_add_results,
4180 &mut fp_is_zero,
4181 &mut fp_is_positive,
4182 &mut fp_is_negative,
4183 &mut fp_not_nan,
4184 &mut fp_gt_comparisons,
4185 &mut fp_lt_comparisons,
4186 &mut fp_conversions,
4187 &mut real_to_fp_conversions,
4188 &mut fp_subtractions,
4189 true, );
4191 }
4192
4193 for &zero_term in &fp_is_zero {
4195 for &(sub_lhs, sub_rhs, sub_result) in &fp_subtractions {
4196 if zero_term == sub_result {
4197 fp_equalities.push((sub_lhs, sub_rhs));
4199 }
4200 for &(eq_lhs, eq_rhs) in fp_equalities.clone().iter() {
4202 if (eq_lhs == zero_term && eq_rhs == sub_result)
4203 || (eq_rhs == zero_term && eq_lhs == sub_result)
4204 {
4205 fp_equalities.push((sub_lhs, sub_rhs));
4206 }
4207 }
4208 }
4209 }
4210
4211 for &(gt_lhs, gt_rhs) in &fp_gt_comparisons {
4214 for &(lt_lhs, lt_rhs) in &fp_lt_comparisons {
4215 if gt_lhs == lt_lhs {
4217 if gt_rhs == lt_rhs {
4219 return true; }
4221 if let (Some(>_val), Some(<_val)) =
4223 (fp_literals.get(>_rhs), fp_literals.get(<_rhs))
4224 {
4225 if (gt_val - lt_val).abs() < f64::EPSILON {
4226 return true; }
4228 }
4229 }
4230 }
4231 }
4232
4233 for &var in &fp_is_zero {
4237 if fp_is_negative.contains(&var) {
4238 for &(eq_lhs, eq_rhs) in &fp_equalities {
4240 let add_term = if eq_lhs == var {
4241 eq_rhs
4242 } else if eq_rhs == var {
4243 eq_lhs
4244 } else {
4245 continue;
4246 };
4247 if let Some(term_data) = manager.get(add_term) {
4248 if let TermKind::FpAdd(_, lhs, rhs) = &term_data.kind {
4249 let lhs_pos_zero =
4251 fp_is_zero.contains(lhs) && fp_is_positive.contains(lhs);
4252 let lhs_neg_zero =
4253 fp_is_zero.contains(lhs) && fp_is_negative.contains(lhs);
4254 let rhs_pos_zero =
4255 fp_is_zero.contains(rhs) && fp_is_positive.contains(rhs);
4256 let rhs_neg_zero =
4257 fp_is_zero.contains(rhs) && fp_is_negative.contains(rhs);
4258
4259 if (lhs_pos_zero && rhs_neg_zero) || (lhs_neg_zero && rhs_pos_zero) {
4260 return true;
4262 }
4263 }
4264 }
4265 }
4266 }
4267 }
4268
4269 for &var in &fp_not_nan {
4271 for &(eq_lhs, eq_rhs) in &fp_equalities {
4273 let div_term = if eq_lhs == var {
4274 eq_rhs
4275 } else if eq_rhs == var {
4276 eq_lhs
4277 } else {
4278 continue;
4279 };
4280 if let Some(term_data) = manager.get(div_term) {
4281 if let TermKind::FpDiv(_, dividend, divisor) = &term_data.kind {
4282 if fp_is_zero.contains(dividend) && fp_is_zero.contains(divisor) {
4284 return true;
4286 }
4287 }
4288 }
4289 }
4290 }
4291
4292 for i in 0..fp_conversions.len() {
4299 for j in (i + 1)..fp_conversions.len() {
4300 let (src1, eb1, sb1, result1) = fp_conversions[i];
4301 let (src2, eb2, sb2, result2) = fp_conversions[j];
4302
4303 if eb1 == eb2 && sb1 == sb2 {
4305 let results_equal = result1 == result2
4307 || fp_equalities.iter().any(|&(l, r)| {
4308 (l == result1 && r == result2) || (l == result2 && r == result1)
4309 });
4310
4311 if results_equal {
4312 let src1_through_smaller = self.source_went_through_smaller_format_check(
4315 src1,
4316 eb1,
4317 sb1,
4318 manager,
4319 &fp_equalities,
4320 );
4321 let src2_direct =
4322 self.is_direct_from_real_value(src2, manager, &fp_equalities);
4323
4324 if src1_through_smaller && src2_direct {
4325 if self.value_loses_precision_check(
4326 src2,
4327 manager,
4328 &fp_equalities,
4329 &real_to_fp_conversions,
4330 ) {
4331 return true;
4332 }
4333 }
4334
4335 let src2_through_smaller = self.source_went_through_smaller_format_check(
4336 src2,
4337 eb2,
4338 sb2,
4339 manager,
4340 &fp_equalities,
4341 );
4342 let src1_direct =
4343 self.is_direct_from_real_value(src1, manager, &fp_equalities);
4344
4345 if src2_through_smaller && src1_direct {
4346 if self.value_loses_precision_check(
4347 src1,
4348 manager,
4349 &fp_equalities,
4350 &real_to_fp_conversions,
4351 ) {
4352 return true;
4353 }
4354 }
4355 }
4356 }
4357 }
4358 }
4359
4360 for &(fp_src, fp_eb, fp_sb, fp_result) in &fp_conversions {
4365 for &(real_arg, real_eb, real_sb, real_result) in &real_to_fp_conversions {
4366 if fp_eb == real_eb && fp_sb == real_sb {
4368 let results_equal = fp_result == real_result
4370 || fp_equalities.iter().any(|&(l, r)| {
4371 (l == fp_result && r == real_result)
4372 || (l == real_result && r == fp_result)
4373 });
4374
4375 if results_equal {
4376 let fp_src_smaller_format =
4380 real_to_fp_conversions.iter().any(
4381 |&(_, src_eb, src_sb, src_result)| {
4382 src_result == fp_src && (src_eb < fp_eb || src_sb < fp_sb)
4383 },
4384 ) || fp_equalities.iter().any(|&(eq_l, eq_r)| {
4385 let check_term = if eq_l == fp_src {
4386 eq_r
4387 } else if eq_r == fp_src {
4388 eq_l
4389 } else {
4390 return false;
4391 };
4392 real_to_fp_conversions.iter().any(
4393 |&(_, src_eb, src_sb, src_result)| {
4394 src_result == check_term
4395 && (src_eb < fp_eb || src_sb < fp_sb)
4396 },
4397 )
4398 });
4399
4400 if fp_src_smaller_format {
4401 if let Some(real_data) = manager.get(real_arg) {
4403 if let TermKind::RealConst(r) = &real_data.kind {
4404 if let Some(val) = r.to_f64() {
4405 let as_f32 = val as f32;
4406 let back_to_f64 = as_f32 as f64;
4407 if (val - back_to_f64).abs() > f64::EPSILON {
4408 return true; }
4410 }
4411 }
4412 }
4413 }
4414 }
4415 }
4416 }
4417 }
4418
4419 for &(small_arg, small_eb, small_sb, small_result) in &real_to_fp_conversions {
4428 let small_arg_is_real = if let Some(d) = manager.get(small_arg) {
4430 matches!(d.kind, TermKind::RealConst(_))
4431 } else {
4432 false
4433 };
4434
4435 if !small_arg_is_real {
4436 continue;
4437 }
4438
4439 for &(large_arg, large_eb, large_sb, large_result_indirect) in &real_to_fp_conversions {
4441 if large_eb <= small_eb && large_sb <= small_sb {
4443 continue;
4444 }
4445
4446 let chain_connected = large_arg == small_result
4448 || fp_equalities.iter().any(|&(l, r)| {
4449 (l == large_arg && r == small_result)
4450 || (l == small_result && r == large_arg)
4451 });
4452
4453 if !chain_connected {
4454 continue;
4455 }
4456
4457 for &(direct_arg, direct_eb, direct_sb, large_result_direct) in
4459 &real_to_fp_conversions
4460 {
4461 if direct_eb != large_eb || direct_sb != large_sb {
4463 continue;
4464 }
4465
4466 let same_original = direct_arg == small_arg || {
4468 if let (Some(d1), Some(d2)) =
4469 (manager.get(small_arg), manager.get(direct_arg))
4470 {
4471 match (&d1.kind, &d2.kind) {
4472 (TermKind::RealConst(v1), TermKind::RealConst(v2)) => {
4473 if v1 == v2 {
4474 true
4475 } else if let (Some(f1), Some(f2)) = (v1.to_f64(), v2.to_f64())
4476 {
4477 (f1 - f2).abs() < 1e-15
4478 } else {
4479 false
4480 }
4481 }
4482 _ => false,
4483 }
4484 } else {
4485 false
4486 }
4487 };
4488
4489 if !same_original {
4490 continue;
4491 }
4492
4493 let results_equal = large_result_indirect == large_result_direct
4495 || fp_equalities.iter().any(|&(l, r)| {
4496 (l == large_result_indirect && r == large_result_direct)
4497 || (l == large_result_direct && r == large_result_indirect)
4498 });
4499
4500 if results_equal {
4501 if let Some(real_data) = manager.get(small_arg) {
4503 if let TermKind::RealConst(r) = &real_data.kind {
4504 if let Some(val) = r.to_f64() {
4505 let as_f32 = val as f32;
4506 let back_to_f64 = as_f32 as f64;
4507 if (val - back_to_f64).abs() > f64::EPSILON {
4508 return true; }
4510 }
4511 }
4512 }
4513 }
4514 }
4515 }
4516 }
4517
4518 for &(small_arg, small_eb, small_sb, small_result) in &real_to_fp_conversions {
4520 let small_arg_is_real = if let Some(d) = manager.get(small_arg) {
4522 matches!(d.kind, TermKind::RealConst(_))
4523 } else {
4524 false
4525 };
4526
4527 if !small_arg_is_real {
4528 continue;
4529 }
4530
4531 for &(fp_src, fp_eb, fp_sb, fp_result) in &fp_conversions {
4533 if fp_eb <= small_eb && fp_sb <= small_sb {
4535 continue;
4536 }
4537
4538 let chain_connected = fp_src == small_result
4540 || fp_equalities.iter().any(|&(l, r)| {
4541 (l == fp_src && r == small_result) || (l == small_result && r == fp_src)
4542 });
4543
4544 if !chain_connected {
4545 continue;
4546 }
4547
4548 for &(direct_arg, direct_eb, direct_sb, large_result_direct) in
4550 &real_to_fp_conversions
4551 {
4552 if direct_eb != fp_eb || direct_sb != fp_sb {
4554 continue;
4555 }
4556
4557 let same_original = direct_arg == small_arg || {
4559 if let (Some(d1), Some(d2)) =
4560 (manager.get(small_arg), manager.get(direct_arg))
4561 {
4562 match (&d1.kind, &d2.kind) {
4563 (TermKind::RealConst(v1), TermKind::RealConst(v2)) => {
4564 if v1 == v2 {
4565 true
4566 } else if let (Some(f1), Some(f2)) = (v1.to_f64(), v2.to_f64())
4567 {
4568 (f1 - f2).abs() < 1e-15
4569 } else {
4570 false
4571 }
4572 }
4573 _ => false,
4574 }
4575 } else {
4576 false
4577 }
4578 };
4579
4580 if !same_original {
4581 continue;
4582 }
4583
4584 let results_equal = fp_result == large_result_direct
4586 || fp_equalities.iter().any(|&(l, r)| {
4587 (l == fp_result && r == large_result_direct)
4588 || (l == large_result_direct && r == fp_result)
4589 });
4590
4591 if results_equal {
4592 if let Some(real_data) = manager.get(small_arg) {
4594 if let TermKind::RealConst(r) = &real_data.kind {
4595 if let Some(val) = r.to_f64() {
4596 let as_f32 = val as f32;
4597 let back_to_f64 = as_f32 as f64;
4598 if (val - back_to_f64).abs() > f64::EPSILON {
4599 return true; }
4601 }
4602 }
4603 }
4604 }
4605 }
4606 }
4607 }
4608
4609 for &(small_arg, small_eb, small_sb, small_result) in &real_to_fp_conversions {
4612 let small_value = if let Some(d) = manager.get(small_arg) {
4614 if let TermKind::RealConst(r) = &d.kind {
4615 r.to_f64()
4616 } else {
4617 None
4618 }
4619 } else {
4620 None
4621 };
4622
4623 let Some(small_val) = small_value else {
4624 continue;
4625 };
4626
4627 let as_small = small_val as f32;
4629 let back_to_large = as_small as f64;
4630 if (small_val - back_to_large).abs() <= f64::EPSILON {
4631 continue; }
4633
4634 for &(large_arg, large_eb, large_sb, large_result) in &real_to_fp_conversions {
4638 if large_eb <= small_eb && large_sb <= small_sb {
4640 continue;
4641 }
4642
4643 let is_chain = large_arg == small_result
4645 || fp_equalities.iter().any(|&(l, r)| {
4646 (l == large_arg && r == small_result)
4647 || (l == small_result && r == large_arg)
4648 });
4649
4650 if !is_chain {
4651 continue;
4652 }
4653
4654 for &(direct_arg, direct_eb, direct_sb, direct_result) in &real_to_fp_conversions {
4657 if direct_eb != large_eb || direct_sb != large_sb {
4658 continue;
4659 }
4660
4661 let direct_val = if let Some(d) = manager.get(direct_arg) {
4663 if let TermKind::RealConst(r) = &d.kind {
4664 r.to_f64()
4665 } else {
4666 None
4667 }
4668 } else {
4669 None
4670 };
4671
4672 let Some(dval) = direct_val else { continue };
4673 if (dval - small_val).abs() > f64::EPSILON {
4674 continue; }
4676
4677 let are_equal = large_result == direct_result
4679 || fp_equalities.iter().any(|&(l, r)| {
4680 (l == large_result && r == direct_result)
4681 || (l == direct_result && r == large_result)
4682 });
4683
4684 if are_equal {
4685 return true; }
4687 }
4688 }
4689
4690 for &(fp_src, fp_eb, fp_sb, fp_result) in &fp_conversions {
4692 if fp_eb <= small_eb && fp_sb <= small_sb {
4694 continue;
4695 }
4696
4697 let is_chain = fp_src == small_result
4699 || fp_equalities.iter().any(|&(l, r)| {
4700 (l == fp_src && r == small_result) || (l == small_result && r == fp_src)
4701 });
4702
4703 if !is_chain {
4704 continue;
4705 }
4706
4707 for &(direct_arg, direct_eb, direct_sb, direct_result) in &real_to_fp_conversions {
4710 if direct_eb != fp_eb || direct_sb != fp_sb {
4711 continue;
4712 }
4713
4714 let direct_val = if let Some(d) = manager.get(direct_arg) {
4716 if let TermKind::RealConst(r) = &d.kind {
4717 r.to_f64()
4718 } else {
4719 None
4720 }
4721 } else {
4722 None
4723 };
4724
4725 let Some(dval) = direct_val else { continue };
4726 if (dval - small_val).abs() > f64::EPSILON {
4727 continue; }
4729
4730 let are_equal = fp_result == direct_result
4732 || fp_equalities.iter().any(|&(l, r)| {
4733 (l == fp_result && r == direct_result)
4734 || (l == direct_result && r == fp_result)
4735 });
4736
4737 if are_equal {
4738 return true; }
4740 }
4741 }
4742 }
4743
4744 for &(eq_lhs, eq_rhs) in &fp_equalities {
4747 let lhs_source = self.find_fp_conversion_source(
4749 eq_lhs,
4750 manager,
4751 &fp_equalities,
4752 &fp_conversions,
4753 &real_to_fp_conversions,
4754 );
4755 let rhs_source = self.find_fp_conversion_source(
4756 eq_rhs,
4757 manager,
4758 &fp_equalities,
4759 &fp_conversions,
4760 &real_to_fp_conversions,
4761 );
4762
4763 if let (Some((lhs_val, lhs_through_small)), Some((rhs_val, rhs_through_small))) =
4765 (lhs_source, rhs_source)
4766 {
4767 if (lhs_val - rhs_val).abs() < 1e-15 {
4769 if lhs_through_small != rhs_through_small {
4771 let as_f32 = lhs_val as f32;
4773 let back_to_f64 = as_f32 as f64;
4774 if (lhs_val - back_to_f64).abs() > f64::EPSILON {
4775 return true; }
4777 }
4778 }
4779 }
4780 }
4781
4782 for &(lt_arg, gt_arg, is_lt) in &fp_comparisons {
4786 if !is_lt {
4787 continue;
4788 }
4789 for (key, result) in &rounding_add_results {
4792 let (op1, op2, rm) = key;
4793 if *result == lt_arg && *rm == RoundingMode::RTP {
4794 let rtn_key = (*op1, *op2, RoundingMode::RTN);
4796 if let Some(&rtn_result) = rounding_add_results.get(&rtn_key) {
4797 if rtn_result == gt_arg {
4798 return true;
4801 }
4802 }
4803 }
4804 }
4805 }
4806
4807 for &(div_result, dividend, divisor, _result_var, _rm) in &fp_divisions {
4811 for &(mul_result, mul_op1, mul_op2, _mul_result_var, _mul_rm) in &fp_multiplications {
4813 let is_div_mul_pattern = (mul_op1 == div_result && mul_op2 == divisor)
4814 || (mul_op2 == div_result && mul_op1 == divisor);
4815 if is_div_mul_pattern {
4816 for &(eq_lhs, eq_rhs) in &fp_equalities {
4818 if (eq_lhs == mul_result && eq_rhs == dividend)
4819 || (eq_rhs == mul_result && eq_lhs == dividend)
4820 {
4821 if let (Some(&div_val), Some(&divis_val)) =
4823 (fp_literals.get(÷nd), fp_literals.get(&divisor))
4824 {
4825 if divis_val != 0.0 {
4826 let exact = div_val / divis_val;
4827 let reconstructed = exact * divis_val;
4828 if (reconstructed - div_val).abs() > f64::EPSILON {
4829 return true;
4831 }
4832 }
4833 }
4834 }
4835 }
4836 }
4837 }
4838 }
4839
4840 for &(small_real_arg, small_eb, small_sb, small_result) in &real_to_fp_conversions {
4844 let small_real_val = if let Some(d) = manager.get(small_real_arg) {
4846 if let TermKind::RealConst(r) = &d.kind {
4847 r.to_f64()
4848 } else {
4849 None
4850 }
4851 } else {
4852 None
4853 };
4854
4855 let Some(real_val) = small_real_val else {
4856 continue;
4857 };
4858
4859 let as_small = real_val as f32;
4861 let back_to_large = as_small as f64;
4862 if (real_val - back_to_large).abs() <= f64::EPSILON {
4863 continue; }
4865
4866 for &(chain_src, chain_eb, chain_sb, chain_result) in &fp_conversions {
4869 let is_chain_src = chain_src == small_result
4871 || fp_equalities.iter().any(|&(l, r)| {
4872 (l == chain_src && r == small_result)
4873 || (l == small_result && r == chain_src)
4874 });
4875
4876 if !is_chain_src || chain_eb <= small_eb || chain_sb <= small_sb {
4877 continue;
4878 }
4879
4880 for &(direct_real_arg, direct_eb, direct_sb, direct_result) in
4883 &real_to_fp_conversions
4884 {
4885 if direct_eb != chain_eb || direct_sb != chain_sb {
4886 continue;
4887 }
4888
4889 let direct_real_val = if let Some(d) = manager.get(direct_real_arg) {
4891 if let TermKind::RealConst(r) = &d.kind {
4892 r.to_f64()
4893 } else {
4894 None
4895 }
4896 } else {
4897 None
4898 };
4899
4900 let Some(direct_val) = direct_real_val else {
4901 continue;
4902 };
4903 if (real_val - direct_val).abs() > f64::EPSILON {
4904 continue; }
4906
4907 let are_transitively_equal = Self::are_terms_equal_transitively(
4910 chain_result,
4911 direct_result,
4912 &fp_equalities,
4913 );
4914
4915 if are_transitively_equal {
4916 return true;
4918 }
4919 }
4920 }
4921 }
4922
4923 false
4924 }
4925
4926 fn source_went_through_smaller_format_check(
4928 &self,
4929 source: TermId,
4930 target_eb: u32,
4931 target_sb: u32,
4932 manager: &TermManager,
4933 equalities: &[(TermId, TermId)],
4934 ) -> bool {
4935 if let Some(term_data) = manager.get(source) {
4936 if let TermKind::FpToFp { eb, sb, .. } = &term_data.kind {
4937 return *eb < target_eb || *sb < target_sb;
4938 }
4939 }
4940 for &(eq_lhs, eq_rhs) in equalities {
4942 let to_check = if eq_lhs == source {
4943 eq_rhs
4944 } else if eq_rhs == source {
4945 eq_lhs
4946 } else {
4947 continue;
4948 };
4949 if let Some(term_data) = manager.get(to_check) {
4950 if let TermKind::FpToFp { eb, sb, .. } = &term_data.kind {
4951 return *eb < target_eb || *sb < target_sb;
4952 }
4953 }
4954 }
4955 false
4956 }
4957
4958 fn is_direct_from_real_value(
4960 &self,
4961 term: TermId,
4962 manager: &TermManager,
4963 equalities: &[(TermId, TermId)],
4964 ) -> bool {
4965 if let Some(term_data) = manager.get(term) {
4966 if matches!(term_data.kind, TermKind::RealToFp { .. }) {
4967 return true;
4968 }
4969 }
4970 for &(eq_lhs, eq_rhs) in equalities {
4971 let to_check = if eq_lhs == term {
4972 eq_rhs
4973 } else if eq_rhs == term {
4974 eq_lhs
4975 } else {
4976 continue;
4977 };
4978 if let Some(term_data) = manager.get(to_check) {
4979 if matches!(term_data.kind, TermKind::RealToFp { .. }) {
4980 return true;
4981 }
4982 }
4983 }
4984 false
4985 }
4986
4987 fn value_loses_precision_check(
4989 &self,
4990 term: TermId,
4991 manager: &TermManager,
4992 equalities: &[(TermId, TermId)],
4993 real_to_fp: &[(TermId, u32, u32, TermId)],
4994 ) -> bool {
4995 if let Some(val) =
4997 self.get_original_real_value_from_term(term, manager, equalities, real_to_fp)
4998 {
4999 let as_f32 = val as f32;
5001 let back_to_f64 = as_f32 as f64;
5002 if (val - back_to_f64).abs() > f64::EPSILON {
5003 return true;
5004 }
5005 }
5006 false
5007 }
5008
5009 fn get_original_real_value_from_term(
5011 &self,
5012 term: TermId,
5013 manager: &TermManager,
5014 equalities: &[(TermId, TermId)],
5015 real_to_fp: &[(TermId, u32, u32, TermId)],
5016 ) -> Option<f64> {
5017 if let Some(term_data) = manager.get(term) {
5019 if let TermKind::RealToFp { arg, .. } = &term_data.kind {
5020 if let Some(arg_data) = manager.get(*arg) {
5021 if let TermKind::RealConst(r) = &arg_data.kind {
5022 return r.to_f64();
5023 }
5024 }
5025 }
5026 }
5027 for &(eq_lhs, eq_rhs) in equalities {
5029 let to_check = if eq_lhs == term {
5030 eq_rhs
5031 } else if eq_rhs == term {
5032 eq_lhs
5033 } else {
5034 continue;
5035 };
5036 if let Some(term_data) = manager.get(to_check) {
5037 if let TermKind::RealToFp { arg, .. } = &term_data.kind {
5038 if let Some(arg_data) = manager.get(*arg) {
5039 if let TermKind::RealConst(r) = &arg_data.kind {
5040 return r.to_f64();
5041 }
5042 }
5043 }
5044 }
5045 }
5046 for &(real_arg, _, _, result) in real_to_fp {
5048 if result == term {
5049 if let Some(arg_data) = manager.get(real_arg) {
5050 if let TermKind::RealConst(r) = &arg_data.kind {
5051 return r.to_f64();
5052 }
5053 }
5054 }
5055 }
5056 None
5057 }
5058
5059 fn find_fp_conversion_source(
5062 &self,
5063 term: TermId,
5064 manager: &TermManager,
5065 equalities: &[(TermId, TermId)],
5066 fp_conversions: &[(TermId, u32, u32, TermId)],
5067 real_to_fp_conversions: &[(TermId, u32, u32, TermId)],
5068 ) -> Option<(f64, bool)> {
5069 let terms_match = |a: TermId, b: TermId| -> bool {
5071 a == b
5072 || equalities
5073 .iter()
5074 .any(|&(l, r)| (l == a && r == b) || (l == b && r == a))
5075 };
5076
5077 let get_real_value = |t: TermId| -> Option<f64> {
5079 if let Some(data) = manager.get(t) {
5080 if let TermKind::RealConst(r) = &data.kind {
5081 return r.to_f64();
5082 }
5083 }
5084 None
5085 };
5086
5087 for &(real_arg, eb, sb, result) in real_to_fp_conversions {
5089 if terms_match(result, term) && eb == 11 && sb == 53 {
5090 if let Some(val) = get_real_value(real_arg) {
5092 return Some((val, false)); }
5094
5095 for &(inner_arg, inner_eb, inner_sb, inner_result) in real_to_fp_conversions {
5099 if terms_match(inner_result, real_arg) && inner_eb < eb && inner_sb < sb {
5100 if let Some(val) = get_real_value(inner_arg) {
5102 return Some((val, true)); }
5104 }
5105 }
5106 }
5107 }
5108
5109 for &(fp_src, eb, sb, result) in fp_conversions {
5111 if terms_match(result, term) && eb == 11 && sb == 53 {
5112 for &(real_arg, src_eb, src_sb, src_result) in real_to_fp_conversions {
5115 if terms_match(fp_src, src_result) && src_eb < 11 && src_sb < 53 {
5116 if let Some(val) = get_real_value(real_arg) {
5118 return Some((val, true)); }
5120 }
5121 }
5122 }
5123 }
5124
5125 for &(eq_lhs, eq_rhs) in equalities {
5127 let other = if eq_lhs == term {
5128 eq_rhs
5129 } else if eq_rhs == term {
5130 eq_lhs
5131 } else {
5132 continue;
5133 };
5134
5135 for &(real_arg, eb, sb, result) in real_to_fp_conversions {
5137 if result == other && eb == 11 && sb == 53 {
5138 if let Some(val) = get_real_value(real_arg) {
5139 return Some((val, false));
5140 }
5141 for &(inner_arg, inner_eb, inner_sb, inner_result) in real_to_fp_conversions {
5143 if terms_match(inner_result, real_arg) && inner_eb < eb && inner_sb < sb {
5144 if let Some(val) = get_real_value(inner_arg) {
5145 return Some((val, true));
5146 }
5147 }
5148 }
5149 }
5150 }
5151
5152 for &(fp_src, eb, sb, result) in fp_conversions {
5154 if result == other && eb == 11 && sb == 53 {
5155 for &(real_arg, src_eb, src_sb, src_result) in real_to_fp_conversions {
5156 if terms_match(fp_src, src_result) && src_eb < 11 && src_sb < 53 {
5157 if let Some(val) = get_real_value(real_arg) {
5158 return Some((val, true));
5159 }
5160 }
5161 }
5162 }
5163 }
5164 }
5165
5166 None
5167 }
5168
5169 #[allow(clippy::too_many_arguments)]
5171 fn collect_fp_constraints_extended(
5172 &self,
5173 term: TermId,
5174 manager: &TermManager,
5175 fp_additions: &mut Vec<(TermId, TermId, TermId, TermId, RoundingMode)>,
5176 fp_divisions: &mut Vec<(TermId, TermId, TermId, TermId, RoundingMode)>,
5177 fp_multiplications: &mut Vec<(TermId, TermId, TermId, TermId, RoundingMode)>,
5178 fp_comparisons: &mut Vec<(TermId, TermId, bool)>,
5179 fp_equalities: &mut Vec<(TermId, TermId)>,
5180 fp_literals: &mut FxHashMap<TermId, f64>,
5181 rounding_add_results: &mut FxHashMap<(TermId, TermId, RoundingMode), TermId>,
5182 fp_is_zero: &mut FxHashSet<TermId>,
5183 fp_is_positive: &mut FxHashSet<TermId>,
5184 fp_is_negative: &mut FxHashSet<TermId>,
5185 fp_not_nan: &mut FxHashSet<TermId>,
5186 fp_gt_comparisons: &mut Vec<(TermId, TermId)>,
5187 fp_lt_comparisons: &mut Vec<(TermId, TermId)>,
5188 fp_conversions: &mut Vec<(TermId, u32, u32, TermId)>,
5189 real_to_fp_conversions: &mut Vec<(TermId, u32, u32, TermId)>,
5190 fp_subtractions: &mut Vec<(TermId, TermId, TermId)>,
5191 in_positive_context: bool,
5192 ) {
5193 let Some(term_data) = manager.get(term) else {
5194 return;
5195 };
5196
5197 match &term_data.kind {
5198 TermKind::FpIsZero(arg) => {
5200 if in_positive_context {
5201 fp_is_zero.insert(*arg);
5202 }
5203 self.collect_fp_constraints_extended_recurse(
5204 *arg,
5205 manager,
5206 fp_additions,
5207 fp_divisions,
5208 fp_multiplications,
5209 fp_comparisons,
5210 fp_equalities,
5211 fp_literals,
5212 rounding_add_results,
5213 fp_is_zero,
5214 fp_is_positive,
5215 fp_is_negative,
5216 fp_not_nan,
5217 fp_gt_comparisons,
5218 fp_lt_comparisons,
5219 fp_conversions,
5220 real_to_fp_conversions,
5221 fp_subtractions,
5222 in_positive_context,
5223 );
5224 }
5225 TermKind::FpIsPositive(arg) => {
5226 if in_positive_context {
5227 fp_is_positive.insert(*arg);
5228 }
5229 self.collect_fp_constraints_extended_recurse(
5230 *arg,
5231 manager,
5232 fp_additions,
5233 fp_divisions,
5234 fp_multiplications,
5235 fp_comparisons,
5236 fp_equalities,
5237 fp_literals,
5238 rounding_add_results,
5239 fp_is_zero,
5240 fp_is_positive,
5241 fp_is_negative,
5242 fp_not_nan,
5243 fp_gt_comparisons,
5244 fp_lt_comparisons,
5245 fp_conversions,
5246 real_to_fp_conversions,
5247 fp_subtractions,
5248 in_positive_context,
5249 );
5250 }
5251 TermKind::FpIsNegative(arg) => {
5252 if in_positive_context {
5253 fp_is_negative.insert(*arg);
5254 }
5255 self.collect_fp_constraints_extended_recurse(
5256 *arg,
5257 manager,
5258 fp_additions,
5259 fp_divisions,
5260 fp_multiplications,
5261 fp_comparisons,
5262 fp_equalities,
5263 fp_literals,
5264 rounding_add_results,
5265 fp_is_zero,
5266 fp_is_positive,
5267 fp_is_negative,
5268 fp_not_nan,
5269 fp_gt_comparisons,
5270 fp_lt_comparisons,
5271 fp_conversions,
5272 real_to_fp_conversions,
5273 fp_subtractions,
5274 in_positive_context,
5275 );
5276 }
5277 TermKind::FpIsNaN(arg) => {
5278 if !in_positive_context {
5280 fp_not_nan.insert(*arg);
5281 }
5282 self.collect_fp_constraints_extended_recurse(
5283 *arg,
5284 manager,
5285 fp_additions,
5286 fp_divisions,
5287 fp_multiplications,
5288 fp_comparisons,
5289 fp_equalities,
5290 fp_literals,
5291 rounding_add_results,
5292 fp_is_zero,
5293 fp_is_positive,
5294 fp_is_negative,
5295 fp_not_nan,
5296 fp_gt_comparisons,
5297 fp_lt_comparisons,
5298 fp_conversions,
5299 real_to_fp_conversions,
5300 fp_subtractions,
5301 in_positive_context,
5302 );
5303 }
5304 TermKind::FpLt(a, b) => {
5306 if in_positive_context {
5307 fp_comparisons.push((*a, *b, true));
5308 fp_lt_comparisons.push((*a, *b));
5309 }
5310 self.collect_fp_constraints_extended_recurse(
5311 *a,
5312 manager,
5313 fp_additions,
5314 fp_divisions,
5315 fp_multiplications,
5316 fp_comparisons,
5317 fp_equalities,
5318 fp_literals,
5319 rounding_add_results,
5320 fp_is_zero,
5321 fp_is_positive,
5322 fp_is_negative,
5323 fp_not_nan,
5324 fp_gt_comparisons,
5325 fp_lt_comparisons,
5326 fp_conversions,
5327 real_to_fp_conversions,
5328 fp_subtractions,
5329 in_positive_context,
5330 );
5331 self.collect_fp_constraints_extended_recurse(
5332 *b,
5333 manager,
5334 fp_additions,
5335 fp_divisions,
5336 fp_multiplications,
5337 fp_comparisons,
5338 fp_equalities,
5339 fp_literals,
5340 rounding_add_results,
5341 fp_is_zero,
5342 fp_is_positive,
5343 fp_is_negative,
5344 fp_not_nan,
5345 fp_gt_comparisons,
5346 fp_lt_comparisons,
5347 fp_conversions,
5348 real_to_fp_conversions,
5349 fp_subtractions,
5350 in_positive_context,
5351 );
5352 }
5353 TermKind::FpGt(a, b) => {
5354 if in_positive_context {
5355 fp_comparisons.push((*b, *a, true)); fp_gt_comparisons.push((*a, *b)); }
5358 self.collect_fp_constraints_extended_recurse(
5359 *a,
5360 manager,
5361 fp_additions,
5362 fp_divisions,
5363 fp_multiplications,
5364 fp_comparisons,
5365 fp_equalities,
5366 fp_literals,
5367 rounding_add_results,
5368 fp_is_zero,
5369 fp_is_positive,
5370 fp_is_negative,
5371 fp_not_nan,
5372 fp_gt_comparisons,
5373 fp_lt_comparisons,
5374 fp_conversions,
5375 real_to_fp_conversions,
5376 fp_subtractions,
5377 in_positive_context,
5378 );
5379 self.collect_fp_constraints_extended_recurse(
5380 *b,
5381 manager,
5382 fp_additions,
5383 fp_divisions,
5384 fp_multiplications,
5385 fp_comparisons,
5386 fp_equalities,
5387 fp_literals,
5388 rounding_add_results,
5389 fp_is_zero,
5390 fp_is_positive,
5391 fp_is_negative,
5392 fp_not_nan,
5393 fp_gt_comparisons,
5394 fp_lt_comparisons,
5395 fp_conversions,
5396 real_to_fp_conversions,
5397 fp_subtractions,
5398 in_positive_context,
5399 );
5400 }
5401 TermKind::Eq(lhs, rhs) => {
5403 fp_equalities.push((*lhs, *rhs));
5404
5405 if let Some(val) = self.get_fp_literal_value_from_eq(*rhs, manager, fp_equalities) {
5407 fp_literals.insert(*lhs, val);
5408 } else if let Some(val) =
5409 self.get_fp_literal_value_from_eq(*lhs, manager, fp_equalities)
5410 {
5411 fp_literals.insert(*rhs, val);
5412 }
5413
5414 if let Some(rhs_data) = manager.get(*rhs) {
5416 match &rhs_data.kind {
5417 TermKind::FpAdd(rm, x, y) => {
5418 fp_additions.push((*lhs, *x, *y, *lhs, *rm));
5419 rounding_add_results.insert((*x, *y, *rm), *lhs);
5420 }
5421 TermKind::FpDiv(rm, x, y) => {
5422 fp_divisions.push((*lhs, *x, *y, *lhs, *rm));
5423 }
5424 TermKind::FpMul(rm, x, y) => {
5425 fp_multiplications.push((*lhs, *x, *y, *lhs, *rm));
5426 }
5427 TermKind::FpSub(_, x, y) => {
5428 fp_subtractions.push((*x, *y, *lhs));
5430 }
5431 TermKind::FpToFp { arg, eb, sb, .. } => {
5432 fp_conversions.push((*arg, *eb, *sb, *lhs));
5433 }
5434 TermKind::RealToFp { arg, eb, sb, .. } => {
5435 real_to_fp_conversions.push((*arg, *eb, *sb, *lhs));
5436 }
5437 _ => {}
5438 }
5439 }
5440 if let Some(lhs_data) = manager.get(*lhs) {
5441 match &lhs_data.kind {
5442 TermKind::FpAdd(rm, x, y) => {
5443 fp_additions.push((*rhs, *x, *y, *rhs, *rm));
5444 rounding_add_results.insert((*x, *y, *rm), *rhs);
5445 }
5446 TermKind::FpDiv(rm, x, y) => {
5447 fp_divisions.push((*rhs, *x, *y, *rhs, *rm));
5448 }
5449 TermKind::FpMul(rm, x, y) => {
5450 fp_multiplications.push((*rhs, *x, *y, *rhs, *rm));
5451 }
5452 TermKind::FpSub(_, x, y) => {
5453 fp_subtractions.push((*x, *y, *rhs));
5454 }
5455 TermKind::FpToFp { arg, eb, sb, .. } => {
5456 fp_conversions.push((*arg, *eb, *sb, *rhs));
5457 }
5458 TermKind::RealToFp { arg, eb, sb, .. } => {
5459 real_to_fp_conversions.push((*arg, *eb, *sb, *rhs));
5460 }
5461 _ => {}
5462 }
5463 }
5464
5465 self.collect_fp_constraints_extended_recurse(
5466 *lhs,
5467 manager,
5468 fp_additions,
5469 fp_divisions,
5470 fp_multiplications,
5471 fp_comparisons,
5472 fp_equalities,
5473 fp_literals,
5474 rounding_add_results,
5475 fp_is_zero,
5476 fp_is_positive,
5477 fp_is_negative,
5478 fp_not_nan,
5479 fp_gt_comparisons,
5480 fp_lt_comparisons,
5481 fp_conversions,
5482 real_to_fp_conversions,
5483 fp_subtractions,
5484 in_positive_context,
5485 );
5486 self.collect_fp_constraints_extended_recurse(
5487 *rhs,
5488 manager,
5489 fp_additions,
5490 fp_divisions,
5491 fp_multiplications,
5492 fp_comparisons,
5493 fp_equalities,
5494 fp_literals,
5495 rounding_add_results,
5496 fp_is_zero,
5497 fp_is_positive,
5498 fp_is_negative,
5499 fp_not_nan,
5500 fp_gt_comparisons,
5501 fp_lt_comparisons,
5502 fp_conversions,
5503 real_to_fp_conversions,
5504 fp_subtractions,
5505 in_positive_context,
5506 );
5507 }
5508 TermKind::FpToFp { arg, eb, sb, .. } => {
5510 fp_conversions.push((*arg, *eb, *sb, term));
5511 self.collect_fp_constraints_extended_recurse(
5512 *arg,
5513 manager,
5514 fp_additions,
5515 fp_divisions,
5516 fp_multiplications,
5517 fp_comparisons,
5518 fp_equalities,
5519 fp_literals,
5520 rounding_add_results,
5521 fp_is_zero,
5522 fp_is_positive,
5523 fp_is_negative,
5524 fp_not_nan,
5525 fp_gt_comparisons,
5526 fp_lt_comparisons,
5527 fp_conversions,
5528 real_to_fp_conversions,
5529 fp_subtractions,
5530 in_positive_context,
5531 );
5532 }
5533 TermKind::RealToFp { arg, eb, sb, .. } => {
5534 real_to_fp_conversions.push((*arg, *eb, *sb, term));
5535 if let Some(arg_data) = manager.get(*arg) {
5537 if let TermKind::RealConst(r) = &arg_data.kind {
5538 if let Some(val) = r.to_f64() {
5539 fp_literals.insert(term, val);
5540 }
5541 }
5542 }
5543 }
5544 TermKind::And(args) => {
5546 for &arg in args {
5547 self.collect_fp_constraints_extended(
5548 arg,
5549 manager,
5550 fp_additions,
5551 fp_divisions,
5552 fp_multiplications,
5553 fp_comparisons,
5554 fp_equalities,
5555 fp_literals,
5556 rounding_add_results,
5557 fp_is_zero,
5558 fp_is_positive,
5559 fp_is_negative,
5560 fp_not_nan,
5561 fp_gt_comparisons,
5562 fp_lt_comparisons,
5563 fp_conversions,
5564 real_to_fp_conversions,
5565 fp_subtractions,
5566 in_positive_context,
5567 );
5568 }
5569 }
5570 TermKind::Or(args) => {
5571 for &arg in args {
5573 self.collect_fp_constraints_extended_recurse(
5574 arg,
5575 manager,
5576 fp_additions,
5577 fp_divisions,
5578 fp_multiplications,
5579 fp_comparisons,
5580 fp_equalities,
5581 fp_literals,
5582 rounding_add_results,
5583 fp_is_zero,
5584 fp_is_positive,
5585 fp_is_negative,
5586 fp_not_nan,
5587 fp_gt_comparisons,
5588 fp_lt_comparisons,
5589 fp_conversions,
5590 real_to_fp_conversions,
5591 fp_subtractions,
5592 in_positive_context,
5593 );
5594 }
5595 }
5596 TermKind::Not(inner) => {
5597 self.collect_fp_constraints_extended(
5599 *inner,
5600 manager,
5601 fp_additions,
5602 fp_divisions,
5603 fp_multiplications,
5604 fp_comparisons,
5605 fp_equalities,
5606 fp_literals,
5607 rounding_add_results,
5608 fp_is_zero,
5609 fp_is_positive,
5610 fp_is_negative,
5611 fp_not_nan,
5612 fp_gt_comparisons,
5613 fp_lt_comparisons,
5614 fp_conversions,
5615 real_to_fp_conversions,
5616 fp_subtractions,
5617 !in_positive_context,
5618 );
5619 }
5620 _ => {}
5621 }
5622 }
5623
5624 #[allow(clippy::too_many_arguments)]
5626 fn collect_fp_constraints_extended_recurse(
5627 &self,
5628 term: TermId,
5629 manager: &TermManager,
5630 fp_additions: &mut Vec<(TermId, TermId, TermId, TermId, RoundingMode)>,
5631 fp_divisions: &mut Vec<(TermId, TermId, TermId, TermId, RoundingMode)>,
5632 fp_multiplications: &mut Vec<(TermId, TermId, TermId, TermId, RoundingMode)>,
5633 fp_comparisons: &mut Vec<(TermId, TermId, bool)>,
5634 fp_equalities: &mut Vec<(TermId, TermId)>,
5635 fp_literals: &mut FxHashMap<TermId, f64>,
5636 rounding_add_results: &mut FxHashMap<(TermId, TermId, RoundingMode), TermId>,
5637 fp_is_zero: &mut FxHashSet<TermId>,
5638 fp_is_positive: &mut FxHashSet<TermId>,
5639 fp_is_negative: &mut FxHashSet<TermId>,
5640 fp_not_nan: &mut FxHashSet<TermId>,
5641 fp_gt_comparisons: &mut Vec<(TermId, TermId)>,
5642 fp_lt_comparisons: &mut Vec<(TermId, TermId)>,
5643 fp_conversions: &mut Vec<(TermId, u32, u32, TermId)>,
5644 real_to_fp_conversions: &mut Vec<(TermId, u32, u32, TermId)>,
5645 fp_subtractions: &mut Vec<(TermId, TermId, TermId)>,
5646 in_positive_context: bool,
5647 ) {
5648 let Some(term_data) = manager.get(term) else {
5649 return;
5650 };
5651
5652 match &term_data.kind {
5654 TermKind::FpToFp { arg, eb, sb, .. } => {
5655 fp_conversions.push((*arg, *eb, *sb, term));
5656 self.collect_fp_constraints_extended_recurse(
5657 *arg,
5658 manager,
5659 fp_additions,
5660 fp_divisions,
5661 fp_multiplications,
5662 fp_comparisons,
5663 fp_equalities,
5664 fp_literals,
5665 rounding_add_results,
5666 fp_is_zero,
5667 fp_is_positive,
5668 fp_is_negative,
5669 fp_not_nan,
5670 fp_gt_comparisons,
5671 fp_lt_comparisons,
5672 fp_conversions,
5673 real_to_fp_conversions,
5674 fp_subtractions,
5675 in_positive_context,
5676 );
5677 }
5678 TermKind::RealToFp { arg, eb, sb, .. } => {
5679 real_to_fp_conversions.push((*arg, *eb, *sb, term));
5680 if let Some(arg_data) = manager.get(*arg) {
5681 if let TermKind::RealConst(r) = &arg_data.kind {
5682 if let Some(val) = r.to_f64() {
5683 fp_literals.insert(term, val);
5684 }
5685 }
5686 }
5687 }
5688 TermKind::And(args) | TermKind::Or(args) => {
5689 for &arg in args {
5690 self.collect_fp_constraints_extended_recurse(
5691 arg,
5692 manager,
5693 fp_additions,
5694 fp_divisions,
5695 fp_multiplications,
5696 fp_comparisons,
5697 fp_equalities,
5698 fp_literals,
5699 rounding_add_results,
5700 fp_is_zero,
5701 fp_is_positive,
5702 fp_is_negative,
5703 fp_not_nan,
5704 fp_gt_comparisons,
5705 fp_lt_comparisons,
5706 fp_conversions,
5707 real_to_fp_conversions,
5708 fp_subtractions,
5709 in_positive_context,
5710 );
5711 }
5712 }
5713 TermKind::Apply { func, args } => {
5715 let func_name = manager.resolve_str(*func);
5716 if func_name.starts_with("(_ to_fp ") || func_name.starts_with("(_to_fp ") {
5718 if let Some((eb, sb)) = Self::parse_to_fp_indices(func_name) {
5720 if args.len() >= 2 {
5721 let arg = args[1];
5724 if let Some(arg_data) = manager.get(arg) {
5726 let is_real_arg = matches!(
5727 arg_data.kind,
5728 TermKind::RealConst(_) | TermKind::IntConst(_)
5729 );
5730 if is_real_arg {
5731 real_to_fp_conversions.push((arg, eb, sb, term));
5733 if let TermKind::RealConst(r) = &arg_data.kind {
5735 if let Some(val) = r.to_f64() {
5736 fp_literals.insert(term, val);
5737 }
5738 } else if let TermKind::IntConst(n) = &arg_data.kind {
5739 if let Some(val) = n.to_i64() {
5740 fp_literals.insert(term, val as f64);
5741 }
5742 }
5743 } else {
5744 fp_conversions.push((arg, eb, sb, term));
5746 }
5747 }
5748 }
5749 }
5750 }
5751 for &arg in args.iter() {
5753 self.collect_fp_constraints_extended_recurse(
5754 arg,
5755 manager,
5756 fp_additions,
5757 fp_divisions,
5758 fp_multiplications,
5759 fp_comparisons,
5760 fp_equalities,
5761 fp_literals,
5762 rounding_add_results,
5763 fp_is_zero,
5764 fp_is_positive,
5765 fp_is_negative,
5766 fp_not_nan,
5767 fp_gt_comparisons,
5768 fp_lt_comparisons,
5769 fp_conversions,
5770 real_to_fp_conversions,
5771 fp_subtractions,
5772 in_positive_context,
5773 );
5774 }
5775 }
5776 _ => {}
5777 }
5778 }
5779
5780 fn parse_to_fp_indices(func_name: &str) -> Option<(u32, u32)> {
5782 let trimmed = func_name
5784 .trim_start_matches("(_ to_fp")
5785 .trim_start_matches("(_to_fp")
5786 .trim();
5787 let trimmed = trimmed.trim_end_matches(')').trim();
5788 let parts: Vec<&str> = trimmed.split_whitespace().collect();
5789 if parts.len() >= 2 {
5790 let eb = parts[0].parse().ok()?;
5791 let sb = parts[1].parse().ok()?;
5792 Some((eb, sb))
5793 } else {
5794 None
5795 }
5796 }
5797
5798 fn are_terms_equal_transitively(
5800 term1: TermId,
5801 term2: TermId,
5802 equalities: &[(TermId, TermId)],
5803 ) -> bool {
5804 if term1 == term2 {
5805 return true;
5806 }
5807
5808 let mut visited = FxHashSet::default();
5810 let mut queue = std::collections::VecDeque::new();
5811 queue.push_back(term1);
5812 visited.insert(term1);
5813
5814 while let Some(current) = queue.pop_front() {
5815 if current == term2 {
5816 return true;
5817 }
5818
5819 for &(l, r) in equalities {
5821 let neighbor = if l == current && !visited.contains(&r) {
5822 Some(r)
5823 } else if r == current && !visited.contains(&l) {
5824 Some(l)
5825 } else {
5826 None
5827 };
5828
5829 if let Some(n) = neighbor {
5830 if n == term2 {
5831 return true;
5832 }
5833 visited.insert(n);
5834 queue.push_back(n);
5835 }
5836 }
5837 }
5838
5839 false
5840 }
5841
5842 fn get_fp_literal_value_from_eq(
5844 &self,
5845 term: TermId,
5846 manager: &TermManager,
5847 equalities: &[(TermId, TermId)],
5848 ) -> Option<f64> {
5849 if let Some(term_data) = manager.get(term) {
5851 if let TermKind::RealToFp { arg, .. } = &term_data.kind {
5852 if let Some(arg_data) = manager.get(*arg) {
5853 if let TermKind::RealConst(r) = &arg_data.kind {
5854 return r.to_f64();
5855 }
5856 }
5857 }
5858 if let TermKind::RealConst(r) = &term_data.kind {
5859 return r.to_f64();
5860 }
5861 if let TermKind::IntConst(n) = &term_data.kind {
5862 return n.to_i64().map(|v| v as f64);
5863 }
5864 }
5865 for &(eq_lhs, eq_rhs) in equalities {
5867 let to_check = if eq_lhs == term {
5868 eq_rhs
5869 } else if eq_rhs == term {
5870 eq_lhs
5871 } else {
5872 continue;
5873 };
5874 if let Some(term_data) = manager.get(to_check) {
5875 if let TermKind::RealToFp { arg, .. } = &term_data.kind {
5876 if let Some(arg_data) = manager.get(*arg) {
5877 if let TermKind::RealConst(r) = &arg_data.kind {
5878 return r.to_f64();
5879 }
5880 }
5881 }
5882 }
5883 }
5884 None
5885 }
5886
5887 #[allow(clippy::too_many_arguments)]
5889 fn collect_fp_constraints(
5890 &self,
5891 term: TermId,
5892 manager: &TermManager,
5893 fp_additions: &mut Vec<(TermId, TermId, TermId, TermId, RoundingMode)>,
5894 fp_divisions: &mut Vec<(TermId, TermId, TermId, TermId, RoundingMode)>,
5895 fp_multiplications: &mut Vec<(TermId, TermId, TermId, TermId, RoundingMode)>,
5896 fp_comparisons: &mut Vec<(TermId, TermId, bool)>,
5897 fp_equalities: &mut Vec<(TermId, TermId)>,
5898 fp_literals: &mut FxHashMap<TermId, f64>,
5899 rounding_add_results: &mut FxHashMap<(TermId, TermId, RoundingMode), TermId>,
5900 ) {
5901 let Some(term_data) = manager.get(term) else {
5902 return;
5903 };
5904
5905 match &term_data.kind {
5906 TermKind::Eq(lhs, rhs) => {
5907 fp_equalities.push((*lhs, *rhs));
5908
5909 if let Some(val) = self.get_fp_literal_value(*rhs, manager) {
5911 fp_literals.insert(*lhs, val);
5912 } else if let Some(val) = self.get_fp_literal_value(*lhs, manager) {
5913 fp_literals.insert(*rhs, val);
5914 }
5915
5916 if let Some(rhs_data) = manager.get(*rhs) {
5918 match &rhs_data.kind {
5919 TermKind::FpAdd(rm, x, y) => {
5920 fp_additions.push((*lhs, *x, *y, *lhs, *rm));
5921 rounding_add_results.insert((*x, *y, *rm), *lhs);
5922 }
5923 TermKind::FpDiv(rm, x, y) => {
5924 fp_divisions.push((*lhs, *x, *y, *lhs, *rm));
5925 }
5926 TermKind::FpMul(rm, x, y) => {
5927 fp_multiplications.push((*lhs, *x, *y, *lhs, *rm));
5928 }
5929 _ => {}
5930 }
5931 }
5932 if let Some(lhs_data) = manager.get(*lhs) {
5933 match &lhs_data.kind {
5934 TermKind::FpAdd(rm, x, y) => {
5935 fp_additions.push((*rhs, *x, *y, *rhs, *rm));
5936 rounding_add_results.insert((*x, *y, *rm), *rhs);
5937 }
5938 TermKind::FpDiv(rm, x, y) => {
5939 fp_divisions.push((*rhs, *x, *y, *rhs, *rm));
5940 }
5941 TermKind::FpMul(rm, x, y) => {
5942 fp_multiplications.push((*rhs, *x, *y, *rhs, *rm));
5943 }
5944 _ => {}
5945 }
5946 }
5947
5948 self.collect_fp_constraints(
5949 *lhs,
5950 manager,
5951 fp_additions,
5952 fp_divisions,
5953 fp_multiplications,
5954 fp_comparisons,
5955 fp_equalities,
5956 fp_literals,
5957 rounding_add_results,
5958 );
5959 self.collect_fp_constraints(
5960 *rhs,
5961 manager,
5962 fp_additions,
5963 fp_divisions,
5964 fp_multiplications,
5965 fp_comparisons,
5966 fp_equalities,
5967 fp_literals,
5968 rounding_add_results,
5969 );
5970 }
5971 TermKind::FpLt(a, b) => {
5972 fp_comparisons.push((*a, *b, true));
5973 }
5974 TermKind::FpGt(a, b) => {
5975 fp_comparisons.push((*b, *a, true)); }
5977 TermKind::And(args) => {
5978 for &arg in args {
5979 self.collect_fp_constraints(
5980 arg,
5981 manager,
5982 fp_additions,
5983 fp_divisions,
5984 fp_multiplications,
5985 fp_comparisons,
5986 fp_equalities,
5987 fp_literals,
5988 rounding_add_results,
5989 );
5990 }
5991 }
5992 TermKind::Or(args) => {
5993 for &arg in args {
5994 self.collect_fp_constraints(
5995 arg,
5996 manager,
5997 fp_additions,
5998 fp_divisions,
5999 fp_multiplications,
6000 fp_comparisons,
6001 fp_equalities,
6002 fp_literals,
6003 rounding_add_results,
6004 );
6005 }
6006 }
6007 TermKind::Not(inner) => {
6008 self.collect_fp_constraints(
6009 *inner,
6010 manager,
6011 fp_additions,
6012 fp_divisions,
6013 fp_multiplications,
6014 fp_comparisons,
6015 fp_equalities,
6016 fp_literals,
6017 rounding_add_results,
6018 );
6019 }
6020 _ => {}
6021 }
6022 }
6023
6024 fn check_dt_constraints(&self, manager: &TermManager) -> bool {
6027 let mut constructor_testers: FxHashMap<TermId, Vec<String>> = FxHashMap::default();
6029 let mut negative_testers: FxHashMap<TermId, Vec<String>> = FxHashMap::default();
6031 let mut constructor_equalities: FxHashMap<TermId, Vec<String>> = FxHashMap::default();
6033 let mut dt_var_equalities: Vec<(TermId, TermId)> = Vec::new();
6035
6036 for &assertion in &self.assertions {
6037 self.collect_dt_constraints_v2(
6038 assertion,
6039 manager,
6040 &mut constructor_testers,
6041 &mut negative_testers,
6042 &mut constructor_equalities,
6043 &mut dt_var_equalities,
6044 true,
6045 );
6046 }
6047
6048 for (_var, testers) in &constructor_testers {
6050 if testers.len() > 1 {
6051 let first = &testers[0];
6054 for tester in testers.iter().skip(1) {
6055 if tester != first {
6056 return true; }
6058 }
6059 }
6060 }
6061
6062 for (var, pos_testers) in &constructor_testers {
6064 if let Some(neg_testers) = negative_testers.get(var) {
6065 for pos in pos_testers {
6066 for neg in neg_testers {
6067 if pos == neg {
6068 return true; }
6070 }
6071 }
6072 }
6073 }
6074
6075 for (_var, constructors) in &constructor_equalities {
6077 if constructors.len() > 1 {
6078 let first = &constructors[0];
6079 for cons in constructors.iter().skip(1) {
6080 if cons != first {
6081 return true; }
6083 }
6084 }
6085 }
6086
6087 for (var, testers) in &constructor_testers {
6089 if let Some(equalities) = constructor_equalities.get(var) {
6090 for tester in testers {
6091 for eq_cons in equalities {
6092 if tester != eq_cons {
6093 return true; }
6095 }
6096 }
6097 }
6098 }
6099
6100 for (var, neg_testers) in &negative_testers {
6102 if let Some(equalities) = constructor_equalities.get(var) {
6103 for neg in neg_testers {
6104 for eq_cons in equalities {
6105 if neg == eq_cons {
6106 return true; }
6108 }
6109 }
6110 }
6111 }
6112
6113 for &(var1, var2) in &dt_var_equalities {
6116 if let Some(pos1) = constructor_testers.get(&var1) {
6118 if let Some(neg2) = negative_testers.get(&var2) {
6119 for p in pos1 {
6120 for n in neg2 {
6121 if p == n {
6122 return true;
6124 }
6125 }
6126 }
6127 }
6128 }
6129 if let Some(pos2) = constructor_testers.get(&var2) {
6131 if let Some(neg1) = negative_testers.get(&var1) {
6132 for p in pos2 {
6133 for n in neg1 {
6134 if p == n {
6135 return true;
6137 }
6138 }
6139 }
6140 }
6141 }
6142
6143 if let Some(pos1) = constructor_testers.get(&var1) {
6145 if let Some(pos2) = constructor_testers.get(&var2) {
6146 for p1 in pos1 {
6147 for p2 in pos2 {
6148 if p1 != p2 {
6149 return true;
6151 }
6152 }
6153 }
6154 }
6155 }
6156
6157 if let Some(eq1) = constructor_equalities.get(&var1) {
6159 if let Some(neg2) = negative_testers.get(&var2) {
6160 for e in eq1 {
6161 for n in neg2 {
6162 if e == n {
6163 return true;
6165 }
6166 }
6167 }
6168 }
6169 }
6170 if let Some(eq2) = constructor_equalities.get(&var2) {
6172 if let Some(neg1) = negative_testers.get(&var1) {
6173 for e in eq2 {
6174 for n in neg1 {
6175 if e == n {
6176 return true;
6178 }
6179 }
6180 }
6181 }
6182 }
6183
6184 if let Some(eq1) = constructor_equalities.get(&var1) {
6186 if let Some(pos2) = constructor_testers.get(&var2) {
6187 for e in eq1 {
6188 for p in pos2 {
6189 if e != p {
6190 return true;
6192 }
6193 }
6194 }
6195 }
6196 }
6197 if let Some(eq2) = constructor_equalities.get(&var2) {
6199 if let Some(pos1) = constructor_testers.get(&var1) {
6200 for e in eq2 {
6201 for p in pos1 {
6202 if e != p {
6203 return true;
6205 }
6206 }
6207 }
6208 }
6209 }
6210
6211 if let Some(eq1) = constructor_equalities.get(&var1) {
6213 if let Some(eq2) = constructor_equalities.get(&var2) {
6214 for e1 in eq1 {
6215 for e2 in eq2 {
6216 if e1 != e2 {
6217 return true;
6219 }
6220 }
6221 }
6222 }
6223 }
6224 }
6225
6226 false
6227 }
6228
6229 fn collect_dt_constraints_v2(
6231 &self,
6232 term: TermId,
6233 manager: &TermManager,
6234 constructor_testers: &mut FxHashMap<TermId, Vec<String>>,
6235 negative_testers: &mut FxHashMap<TermId, Vec<String>>,
6236 constructor_equalities: &mut FxHashMap<TermId, Vec<String>>,
6237 dt_var_equalities: &mut Vec<(TermId, TermId)>,
6238 in_positive_context: bool,
6239 ) {
6240 let Some(term_data) = manager.get(term) else {
6241 return;
6242 };
6243
6244 match &term_data.kind {
6245 TermKind::DtTester { constructor, arg } => {
6246 let cons_name = manager.resolve_str(*constructor).to_string();
6247 if in_positive_context {
6248 constructor_testers.entry(*arg).or_default().push(cons_name);
6250 } else {
6251 negative_testers.entry(*arg).or_default().push(cons_name);
6253 }
6254 }
6255 TermKind::Eq(lhs, rhs) => {
6256 if in_positive_context {
6257 if let Some(rhs_data) = manager.get(*rhs) {
6259 if let TermKind::DtConstructor { constructor, .. } = &rhs_data.kind {
6260 if self.is_dt_variable(*lhs, manager) {
6261 constructor_equalities
6262 .entry(*lhs)
6263 .or_default()
6264 .push(manager.resolve_str(*constructor).to_string());
6265 }
6266 }
6267 }
6268 if let Some(lhs_data) = manager.get(*lhs) {
6269 if let TermKind::DtConstructor { constructor, .. } = &lhs_data.kind {
6270 if self.is_dt_variable(*rhs, manager) {
6271 constructor_equalities
6272 .entry(*rhs)
6273 .or_default()
6274 .push(manager.resolve_str(*constructor).to_string());
6275 }
6276 }
6277 }
6278
6279 if self.is_dt_variable(*lhs, manager) && self.is_dt_variable(*rhs, manager) {
6281 dt_var_equalities.push((*lhs, *rhs));
6282 }
6283 }
6284
6285 self.collect_dt_constraints_v2(
6286 *lhs,
6287 manager,
6288 constructor_testers,
6289 negative_testers,
6290 constructor_equalities,
6291 dt_var_equalities,
6292 in_positive_context,
6293 );
6294 self.collect_dt_constraints_v2(
6295 *rhs,
6296 manager,
6297 constructor_testers,
6298 negative_testers,
6299 constructor_equalities,
6300 dt_var_equalities,
6301 in_positive_context,
6302 );
6303 }
6304 TermKind::And(args) => {
6305 for &arg in args {
6306 self.collect_dt_constraints_v2(
6307 arg,
6308 manager,
6309 constructor_testers,
6310 negative_testers,
6311 constructor_equalities,
6312 dt_var_equalities,
6313 in_positive_context,
6314 );
6315 }
6316 }
6317 TermKind::Or(_args) => {
6318 }
6321 TermKind::Not(inner) => {
6322 self.collect_dt_constraints_v2(
6324 *inner,
6325 manager,
6326 constructor_testers,
6327 negative_testers,
6328 constructor_equalities,
6329 dt_var_equalities,
6330 !in_positive_context,
6331 );
6332 }
6333 _ => {}
6334 }
6335 }
6336
6337 fn collect_dt_constraints(
6339 &self,
6340 term: TermId,
6341 manager: &TermManager,
6342 constructor_testers: &mut FxHashMap<TermId, Vec<String>>,
6343 constructor_equalities: &mut FxHashMap<TermId, Vec<String>>,
6344 ) {
6345 self.collect_dt_constraints_inner(
6346 term,
6347 manager,
6348 constructor_testers,
6349 constructor_equalities,
6350 true,
6351 );
6352 }
6353
6354 fn collect_dt_constraints_inner(
6355 &self,
6356 term: TermId,
6357 manager: &TermManager,
6358 constructor_testers: &mut FxHashMap<TermId, Vec<String>>,
6359 constructor_equalities: &mut FxHashMap<TermId, Vec<String>>,
6360 in_positive_context: bool,
6361 ) {
6362 let Some(term_data) = manager.get(term) else {
6363 return;
6364 };
6365
6366 match &term_data.kind {
6367 TermKind::DtTester { constructor, arg } => {
6368 if in_positive_context {
6370 constructor_testers
6371 .entry(*arg)
6372 .or_default()
6373 .push(manager.resolve_str(*constructor).to_string());
6374 }
6375 }
6376 TermKind::Eq(lhs, rhs) => {
6377 if in_positive_context {
6379 if let Some(rhs_data) = manager.get(*rhs) {
6380 if let TermKind::DtConstructor { constructor, .. } = &rhs_data.kind {
6381 if self.is_dt_variable(*lhs, manager) {
6382 constructor_equalities
6383 .entry(*lhs)
6384 .or_default()
6385 .push(manager.resolve_str(*constructor).to_string());
6386 }
6387 }
6388 }
6389 if let Some(lhs_data) = manager.get(*lhs) {
6390 if let TermKind::DtConstructor { constructor, .. } = &lhs_data.kind {
6391 if self.is_dt_variable(*rhs, manager) {
6392 constructor_equalities
6393 .entry(*rhs)
6394 .or_default()
6395 .push(manager.resolve_str(*constructor).to_string());
6396 }
6397 }
6398 }
6399 }
6400
6401 self.collect_dt_constraints_inner(
6402 *lhs,
6403 manager,
6404 constructor_testers,
6405 constructor_equalities,
6406 in_positive_context,
6407 );
6408 self.collect_dt_constraints_inner(
6409 *rhs,
6410 manager,
6411 constructor_testers,
6412 constructor_equalities,
6413 in_positive_context,
6414 );
6415 }
6416 TermKind::And(args) => {
6417 for &arg in args {
6418 self.collect_dt_constraints_inner(
6419 arg,
6420 manager,
6421 constructor_testers,
6422 constructor_equalities,
6423 in_positive_context,
6424 );
6425 }
6426 }
6427 TermKind::Or(_args) => {
6428 }
6431 TermKind::Not(inner) => {
6432 self.collect_dt_constraints_inner(
6434 *inner,
6435 manager,
6436 constructor_testers,
6437 constructor_equalities,
6438 !in_positive_context,
6439 );
6440 }
6441 _ => {}
6442 }
6443 }
6444
6445 fn is_dt_variable(&self, term: TermId, manager: &TermManager) -> bool {
6447 let Some(term_data) = manager.get(term) else {
6448 return false;
6449 };
6450 matches!(term_data.kind, TermKind::Var(_))
6451 }
6452
6453 fn check_array_constraints(&self, manager: &TermManager) -> bool {
6456 let mut select_values: FxHashMap<(TermId, TermId), TermId> = FxHashMap::default();
6458 let mut store_select_same_index: Vec<(TermId, TermId, TermId, TermId)> = Vec::new(); let mut array_equalities: Vec<(TermId, TermId)> = Vec::new();
6462 let mut select_assertions: Vec<(TermId, TermId)> = Vec::new();
6464
6465 for &assertion in &self.assertions {
6466 self.collect_array_constraints(
6467 assertion,
6468 manager,
6469 &mut select_values,
6470 &mut store_select_same_index,
6471 &mut array_equalities,
6472 &mut select_assertions,
6473 );
6474 }
6475
6476 for &(_array, _index, stored_val, result) in &store_select_same_index {
6481 if result != stored_val {
6482 if self.are_different_values(stored_val, result, manager) {
6484 return true; }
6486 }
6487 }
6488
6489 for &(select_term, asserted_value) in &select_assertions {
6493 if let Some(evaluated_value) = self.evaluate_select_axiom(select_term, manager) {
6494 if evaluated_value != asserted_value {
6495 if self.are_different_values(evaluated_value, asserted_value, manager) {
6497 return true; }
6499 }
6500 }
6501 }
6502
6503 for &(array_a, array_b) in &array_equalities {
6506 for (&(sel_array, sel_index), &sel_val) in &select_values {
6508 if sel_array == array_a {
6509 if let Some(&other_val) = select_values.get(&(array_b, sel_index)) {
6511 if sel_val != other_val {
6512 if self.are_different_values(sel_val, other_val, manager) {
6514 return true;
6515 }
6516 }
6517 }
6518 }
6519 }
6520 for &assertion in &self.assertions {
6522 if self.is_select_inequality_assertion(assertion, array_a, array_b, manager) {
6523 return true;
6524 }
6525 }
6526 }
6527
6528 false
6529 }
6530
6531 fn evaluate_select_axiom(&self, term: TermId, manager: &TermManager) -> Option<TermId> {
6535 let term_data = manager.get(term)?;
6536
6537 if let TermKind::Select(array, index) = &term_data.kind {
6538 let simplified_array = self.simplify_array_term(*array, manager);
6540
6541 if let Some(simplified_data) = manager.get(simplified_array) {
6543 if let TermKind::Store(_base, store_idx, stored_val) = &simplified_data.kind {
6544 if self.terms_equal_simple(*store_idx, *index, manager) {
6545 return Some(
6548 self.evaluate_select_axiom(*stored_val, manager)
6549 .unwrap_or(*stored_val),
6550 );
6551 }
6552 }
6553 }
6554
6555 if let Some(array_data) = manager.get(*array) {
6557 if let TermKind::Store(_base, store_idx, stored_val) = &array_data.kind {
6558 if self.terms_equal_simple(*store_idx, *index, manager) {
6559 return Some(
6561 self.evaluate_select_axiom(*stored_val, manager)
6562 .unwrap_or(*stored_val),
6563 );
6564 }
6565 }
6566 }
6567 }
6568
6569 None
6570 }
6571
6572 fn simplify_array_term(&self, term: TermId, manager: &TermManager) -> TermId {
6575 let Some(term_data) = manager.get(term) else {
6576 return term;
6577 };
6578
6579 if let TermKind::Select(array, index) = &term_data.kind {
6580 if let Some(array_data) = manager.get(*array) {
6582 if let TermKind::Store(_base, store_idx, stored_val) = &array_data.kind {
6583 if self.terms_equal_simple(*store_idx, *index, manager) {
6584 return self.simplify_array_term(*stored_val, manager);
6587 }
6588 }
6589 }
6590 }
6591
6592 term
6593 }
6594
6595 fn are_different_values(&self, a: TermId, b: TermId, manager: &TermManager) -> bool {
6597 if a == b {
6598 return false;
6599 }
6600 let (Some(a_data), Some(b_data)) = (manager.get(a), manager.get(b)) else {
6601 return false;
6602 };
6603 match (&a_data.kind, &b_data.kind) {
6604 (TermKind::IntConst(s1), TermKind::IntConst(s2)) => s1 != s2,
6605 (
6606 TermKind::BitVecConst {
6607 value: v1,
6608 width: w1,
6609 },
6610 TermKind::BitVecConst {
6611 value: v2,
6612 width: w2,
6613 },
6614 ) => w1 == w2 && v1 != v2,
6615 (TermKind::RealConst(r1), TermKind::RealConst(r2)) => r1 != r2,
6616 _ => false,
6617 }
6618 }
6619
6620 fn collect_array_constraints(
6623 &self,
6624 term: TermId,
6625 manager: &TermManager,
6626 select_values: &mut FxHashMap<(TermId, TermId), TermId>,
6627 store_select_same_index: &mut Vec<(TermId, TermId, TermId, TermId)>,
6628 array_equalities: &mut Vec<(TermId, TermId)>,
6629 select_assertions: &mut Vec<(TermId, TermId)>,
6630 ) {
6631 self.collect_array_constraints_inner(
6632 term,
6633 manager,
6634 select_values,
6635 store_select_same_index,
6636 array_equalities,
6637 select_assertions,
6638 true,
6639 );
6640 }
6641
6642 fn collect_array_constraints_inner(
6643 &self,
6644 term: TermId,
6645 manager: &TermManager,
6646 select_values: &mut FxHashMap<(TermId, TermId), TermId>,
6647 store_select_same_index: &mut Vec<(TermId, TermId, TermId, TermId)>,
6648 array_equalities: &mut Vec<(TermId, TermId)>,
6649 select_assertions: &mut Vec<(TermId, TermId)>,
6650 in_positive_context: bool,
6651 ) {
6652 let Some(term_data) = manager.get(term) else {
6653 return;
6654 };
6655
6656 match &term_data.kind {
6657 TermKind::Eq(lhs, rhs) => {
6658 if in_positive_context {
6662 if self.is_array_variable(*lhs, manager)
6663 && self.is_array_variable(*rhs, manager)
6664 {
6665 array_equalities.push((*lhs, *rhs));
6666 }
6667 }
6668
6669 if in_positive_context {
6671 if let Some((array, index)) = self.extract_select(*lhs, manager) {
6672 select_values.insert((array, index), *rhs);
6673 select_assertions.push((*lhs, *rhs));
6675 }
6676 if let Some((array, index)) = self.extract_select(*rhs, manager) {
6677 select_values.insert((array, index), *lhs);
6678 select_assertions.push((*rhs, *lhs));
6680 }
6681
6682 if let Some((inner_array, outer_index)) = self.extract_select(*lhs, manager) {
6684 if let Some((base_array, store_index, stored_val)) =
6685 self.extract_store(inner_array, manager)
6686 {
6687 if self.terms_equal_simple(outer_index, store_index, manager) {
6689 store_select_same_index.push((
6690 base_array,
6691 store_index,
6692 stored_val,
6693 *rhs,
6694 ));
6695 }
6696 }
6697 }
6698 if let Some((inner_array, outer_index)) = self.extract_select(*rhs, manager) {
6699 if let Some((base_array, store_index, stored_val)) =
6700 self.extract_store(inner_array, manager)
6701 {
6702 if self.terms_equal_simple(outer_index, store_index, manager) {
6703 store_select_same_index.push((
6704 base_array,
6705 store_index,
6706 stored_val,
6707 *lhs,
6708 ));
6709 }
6710 }
6711 }
6712 }
6713
6714 self.collect_array_constraints_inner(
6715 *lhs,
6716 manager,
6717 select_values,
6718 store_select_same_index,
6719 array_equalities,
6720 select_assertions,
6721 in_positive_context,
6722 );
6723 self.collect_array_constraints_inner(
6724 *rhs,
6725 manager,
6726 select_values,
6727 store_select_same_index,
6728 array_equalities,
6729 select_assertions,
6730 in_positive_context,
6731 );
6732 }
6733 TermKind::And(args) => {
6734 for &arg in args {
6735 self.collect_array_constraints_inner(
6736 arg,
6737 manager,
6738 select_values,
6739 store_select_same_index,
6740 array_equalities,
6741 select_assertions,
6742 in_positive_context,
6743 );
6744 }
6745 }
6746 TermKind::Or(_args) => {
6747 }
6749 TermKind::Not(inner) => {
6750 self.collect_array_constraints_inner(
6752 *inner,
6753 manager,
6754 select_values,
6755 store_select_same_index,
6756 array_equalities,
6757 select_assertions,
6758 !in_positive_context,
6759 );
6760 }
6761 _ => {}
6762 }
6763 }
6764
6765 fn is_array_variable(&self, term: TermId, manager: &TermManager) -> bool {
6767 let Some(term_data) = manager.get(term) else {
6768 return false;
6769 };
6770 if let TermKind::Var(_) = &term_data.kind {
6771 if let Some(sort) = manager.sorts.get(term_data.sort) {
6773 return matches!(sort.kind, oxiz_core::SortKind::Array { .. });
6774 }
6775 }
6776 false
6777 }
6778
6779 fn extract_select(&self, term: TermId, manager: &TermManager) -> Option<(TermId, TermId)> {
6781 let term_data = manager.get(term)?;
6782 if let TermKind::Select(array, index) = &term_data.kind {
6783 Some((*array, *index))
6784 } else {
6785 None
6786 }
6787 }
6788
6789 fn extract_store(
6791 &self,
6792 term: TermId,
6793 manager: &TermManager,
6794 ) -> Option<(TermId, TermId, TermId)> {
6795 let term_data = manager.get(term)?;
6796 if let TermKind::Store(array, index, value) = &term_data.kind {
6797 Some((*array, *index, *value))
6798 } else {
6799 None
6800 }
6801 }
6802
6803 fn terms_equal_simple(&self, a: TermId, b: TermId, manager: &TermManager) -> bool {
6805 if a == b {
6806 return true;
6807 }
6808 let (Some(a_data), Some(b_data)) = (manager.get(a), manager.get(b)) else {
6809 return false;
6810 };
6811 match (&a_data.kind, &b_data.kind) {
6812 (TermKind::IntConst(s1), TermKind::IntConst(s2)) => s1 == s2,
6813 _ => false,
6814 }
6815 }
6816
6817 fn asserts_equality(
6819 &self,
6820 assertion: TermId,
6821 term1: TermId,
6822 term2: TermId,
6823 manager: &TermManager,
6824 ) -> bool {
6825 let Some(assertion_data) = manager.get(assertion) else {
6826 return false;
6827 };
6828 if let TermKind::Eq(lhs, rhs) = &assertion_data.kind {
6829 (*lhs == term1 && *rhs == term2) || (*lhs == term2 && *rhs == term1)
6830 } else {
6831 false
6832 }
6833 }
6834
6835 fn is_select_inequality_assertion(
6837 &self,
6838 assertion: TermId,
6839 array_a: TermId,
6840 array_b: TermId,
6841 manager: &TermManager,
6842 ) -> bool {
6843 let Some(assertion_data) = manager.get(assertion) else {
6844 return false;
6845 };
6846 if let TermKind::Not(inner) = &assertion_data.kind {
6847 let Some(inner_data) = manager.get(*inner) else {
6848 return false;
6849 };
6850 if let TermKind::Eq(lhs, rhs) = &inner_data.kind {
6851 if let (Some((sel_a, idx_a)), Some((sel_b, idx_b))) = (
6853 self.extract_select(*lhs, manager),
6854 self.extract_select(*rhs, manager),
6855 ) {
6856 if ((sel_a == array_a && sel_b == array_b)
6857 || (sel_a == array_b && sel_b == array_a))
6858 && self.terms_equal_simple(idx_a, idx_b, manager)
6859 {
6860 return true;
6861 }
6862 }
6863 }
6864 }
6865 false
6866 }
6867
6868 fn check_bv_constraints(&self, manager: &TermManager) -> bool {
6871 let mut bv_values: FxHashMap<TermId, num_bigint::BigInt> = FxHashMap::default();
6873 let mut bv_or_constraints: Vec<(TermId, TermId, TermId)> = Vec::new(); let mut bv_sub_constraints: Vec<(TermId, TermId, TermId)> = Vec::new(); let mut bv_urem_constraints: Vec<(TermId, TermId, TermId)> = Vec::new(); let mut bv_not_constraints: Vec<(TermId, TermId)> = Vec::new(); let mut bv_xor_constraints: Vec<(TermId, TermId, TermId)> = Vec::new(); let mut bv_widths: FxHashMap<TermId, u32> = FxHashMap::default();
6879
6880 for &assertion in &self.assertions {
6881 self.collect_bv_constraints(
6882 assertion,
6883 manager,
6884 &mut bv_values,
6885 &mut bv_or_constraints,
6886 &mut bv_sub_constraints,
6887 &mut bv_urem_constraints,
6888 &mut bv_not_constraints,
6889 &mut bv_xor_constraints,
6890 &mut bv_widths,
6891 );
6892 }
6893
6894 for &(result, a, b) in &bv_or_constraints {
6897 if let (Some(a_val), Some(b_val), Some(result_val)) =
6898 (bv_values.get(&a), bv_values.get(&b), bv_values.get(&result))
6899 {
6900 let computed = a_val | b_val;
6901 if &computed != result_val {
6902 return true;
6903 }
6904 }
6905 }
6906
6907 for &(result1, x1, y1) in &bv_sub_constraints {
6911 for &(result2, x2, y2) in &bv_sub_constraints {
6912 if x1 == y2 && y1 == x2 && x1 != y1 {
6914 if let (Some(r1), Some(r2)) = (bv_values.get(&result1), bv_values.get(&result2))
6915 {
6916 let width = bv_widths.get(&result1).copied().unwrap_or(32);
6918 let modulus = num_bigint::BigInt::from(1u64) << width;
6919 let sum = (r1 + r2) % &modulus;
6920 if sum != num_bigint::BigInt::from(0) {
6921 return true;
6922 }
6923 }
6924 }
6925 }
6926 }
6927
6928 for &(result, _x, y) in &bv_urem_constraints {
6931 if let (Some(r_val), Some(y_val)) = (bv_values.get(&result), bv_values.get(&y)) {
6932 if y_val > &num_bigint::BigInt::from(0) && r_val >= y_val {
6933 return true;
6934 }
6935 }
6936 }
6937
6938 for &(_not_result, not_arg) in &bv_not_constraints {
6945 for &(xor_result, xor_a, xor_b) in &bv_xor_constraints {
6946 if xor_a == not_arg || xor_b == not_arg {
6949 if let Some(xor_val) = bv_values.get(&xor_result) {
6953 let width = bv_widths.get(&xor_result).copied().unwrap_or(8);
6955 let all_ones = (num_bigint::BigInt::from(1u64) << width) - 1;
6956 if xor_val == &all_ones {
6957 }
6960 }
6961 }
6962 }
6963 }
6964
6965 false
6966 }
6967
6968 #[allow(clippy::too_many_arguments)]
6970 fn collect_bv_constraints(
6971 &self,
6972 term: TermId,
6973 manager: &TermManager,
6974 bv_values: &mut FxHashMap<TermId, num_bigint::BigInt>,
6975 bv_or_constraints: &mut Vec<(TermId, TermId, TermId)>,
6976 bv_sub_constraints: &mut Vec<(TermId, TermId, TermId)>,
6977 bv_urem_constraints: &mut Vec<(TermId, TermId, TermId)>,
6978 bv_not_constraints: &mut Vec<(TermId, TermId)>,
6979 bv_xor_constraints: &mut Vec<(TermId, TermId, TermId)>,
6980 bv_widths: &mut FxHashMap<TermId, u32>,
6981 ) {
6982 let Some(term_data) = manager.get(term) else {
6983 return;
6984 };
6985
6986 match &term_data.kind {
6987 TermKind::Eq(lhs, rhs) => {
6988 if let Some((val, width)) = self.get_bv_literal_value(*rhs, manager) {
6990 bv_values.insert(*lhs, val.clone());
6991 bv_widths.insert(*lhs, width);
6992 bv_values.insert(*rhs, val);
6993 bv_widths.insert(*rhs, width);
6994 } else if let Some((val, width)) = self.get_bv_literal_value(*lhs, manager) {
6995 bv_values.insert(*rhs, val.clone());
6996 bv_widths.insert(*rhs, width);
6997 bv_values.insert(*lhs, val);
6998 bv_widths.insert(*lhs, width);
6999 }
7000
7001 if let Some(rhs_data) = manager.get(*rhs) {
7003 match &rhs_data.kind {
7004 TermKind::BvOr(a, b) => {
7005 bv_or_constraints.push((*lhs, *a, *b));
7006 }
7007 TermKind::BvSub(x, y) => {
7008 bv_sub_constraints.push((*lhs, *x, *y));
7009 }
7010 TermKind::BvUrem(x, y) => {
7011 bv_urem_constraints.push((*lhs, *x, *y));
7012 }
7013 TermKind::BvNot(x) => {
7014 bv_not_constraints.push((*lhs, *x));
7015 }
7016 TermKind::BvXor(x, y) => {
7017 bv_xor_constraints.push((*lhs, *x, *y));
7018 }
7019 _ => {}
7020 }
7021 }
7022 if let Some(lhs_data) = manager.get(*lhs) {
7023 match &lhs_data.kind {
7024 TermKind::BvOr(a, b) => {
7025 bv_or_constraints.push((*rhs, *a, *b));
7026 }
7027 TermKind::BvSub(x, y) => {
7028 bv_sub_constraints.push((*rhs, *x, *y));
7029 }
7030 TermKind::BvUrem(x, y) => {
7031 bv_urem_constraints.push((*rhs, *x, *y));
7032 }
7033 TermKind::BvNot(x) => {
7034 bv_not_constraints.push((*rhs, *x));
7035 }
7036 TermKind::BvXor(x, y) => {
7037 bv_xor_constraints.push((*rhs, *x, *y));
7038 }
7039 _ => {}
7040 }
7041 }
7042
7043 self.collect_bv_constraints(
7044 *lhs,
7045 manager,
7046 bv_values,
7047 bv_or_constraints,
7048 bv_sub_constraints,
7049 bv_urem_constraints,
7050 bv_not_constraints,
7051 bv_xor_constraints,
7052 bv_widths,
7053 );
7054 self.collect_bv_constraints(
7055 *rhs,
7056 manager,
7057 bv_values,
7058 bv_or_constraints,
7059 bv_sub_constraints,
7060 bv_urem_constraints,
7061 bv_not_constraints,
7062 bv_xor_constraints,
7063 bv_widths,
7064 );
7065 }
7066 TermKind::And(args) => {
7067 for &arg in args {
7068 self.collect_bv_constraints(
7069 arg,
7070 manager,
7071 bv_values,
7072 bv_or_constraints,
7073 bv_sub_constraints,
7074 bv_urem_constraints,
7075 bv_not_constraints,
7076 bv_xor_constraints,
7077 bv_widths,
7078 );
7079 }
7080 }
7081 TermKind::Or(args) => {
7082 for &arg in args {
7083 self.collect_bv_constraints(
7084 arg,
7085 manager,
7086 bv_values,
7087 bv_or_constraints,
7088 bv_sub_constraints,
7089 bv_urem_constraints,
7090 bv_not_constraints,
7091 bv_xor_constraints,
7092 bv_widths,
7093 );
7094 }
7095 }
7096 TermKind::Not(inner) => {
7097 self.collect_bv_constraints(
7098 *inner,
7099 manager,
7100 bv_values,
7101 bv_or_constraints,
7102 bv_sub_constraints,
7103 bv_urem_constraints,
7104 bv_not_constraints,
7105 bv_xor_constraints,
7106 bv_widths,
7107 );
7108 }
7109 _ => {}
7110 }
7111 }
7112
7113 fn get_bv_literal_value(
7115 &self,
7116 term: TermId,
7117 manager: &TermManager,
7118 ) -> Option<(num_bigint::BigInt, u32)> {
7119 let term_data = manager.get(term)?;
7120 if let TermKind::BitVecConst { value, width } = &term_data.kind {
7121 Some((value.clone(), *width))
7122 } else {
7123 None
7124 }
7125 }
7126
7127 fn get_fp_literal_value(&self, term: TermId, manager: &TermManager) -> Option<f64> {
7129 let term_data = manager.get(term)?;
7130 match &term_data.kind {
7131 TermKind::RealToFp { arg, .. } => {
7133 let arg_data = manager.get(*arg)?;
7135 if let TermKind::RealConst(r) = &arg_data.kind {
7136 r.to_f64()
7137 } else {
7138 None
7139 }
7140 }
7141 TermKind::FpLit {
7143 sign,
7144 exp,
7145 sig,
7146 eb,
7147 sb,
7148 } => {
7149 if *eb == 8 && *sb == 24 {
7152 let sign_bit = if *sign { 1u32 << 31 } else { 0 };
7154 let exp_bits = (exp.to_u32().unwrap_or(0) & 0xFF) << 23;
7155 let sig_bits = sig.to_u32().unwrap_or(0) & 0x7FFFFF;
7156 let bits = sign_bit | exp_bits | sig_bits;
7157 Some(f32::from_bits(bits) as f64)
7158 } else if *eb == 11 && *sb == 53 {
7159 let sign_bit = if *sign { 1u64 << 63 } else { 0 };
7161 let exp_bits = (exp.to_u64().unwrap_or(0) & 0x7FF) << 52;
7162 let sig_bits = sig.to_u64().unwrap_or(0) & 0xFFFFFFFFFFFFF;
7163 let bits = sign_bit | exp_bits | sig_bits;
7164 Some(f64::from_bits(bits))
7165 } else {
7166 None
7167 }
7168 }
7169 _ => None,
7170 }
7171 }
7172
7173 pub fn check_with_assumptions(
7176 &mut self,
7177 assumptions: &[TermId],
7178 manager: &mut TermManager,
7179 ) -> SolverResult {
7180 self.push();
7182
7183 for &assumption in assumptions {
7185 self.assert(assumption, manager);
7186 }
7187
7188 let result = self.check(manager);
7190
7191 self.pop();
7193
7194 result
7195 }
7196
7197 pub fn check_sat_only(&mut self, manager: &mut TermManager) -> SolverResult {
7200 if self.assertions.is_empty() {
7201 return SolverResult::Sat;
7202 }
7203
7204 match self.sat.solve() {
7205 SatResult::Sat => {
7206 self.build_model(manager);
7207 SolverResult::Sat
7208 }
7209 SatResult::Unsat => SolverResult::Unsat,
7210 SatResult::Unknown => SolverResult::Unknown,
7211 }
7212 }
7213
7214 fn build_model(&mut self, manager: &mut TermManager) {
7216 let mut model = Model::new();
7217 let sat_model = self.sat.model();
7218
7219 for (&term, &var) in &self.term_to_var {
7221 let val = sat_model.get(var.index()).copied();
7222 if let Some(v) = val {
7223 let bool_val = if v.is_true() {
7224 manager.mk_true()
7225 } else if v.is_false() {
7226 manager.mk_false()
7227 } else {
7228 continue;
7229 };
7230 model.set(term, bool_val);
7231 }
7232 }
7233
7234 for (&var, constraint) in &self.var_to_constraint {
7237 let is_true = sat_model
7239 .get(var.index())
7240 .copied()
7241 .is_some_and(|v| v.is_true());
7242
7243 if !is_true {
7244 continue;
7245 }
7246
7247 if let Constraint::Eq(lhs, rhs) = constraint {
7248 let (var_term, const_term) =
7250 if self.arith_terms.contains(lhs) || self.bv_terms.contains(lhs) {
7251 (*lhs, *rhs)
7252 } else if self.arith_terms.contains(rhs) || self.bv_terms.contains(rhs) {
7253 (*rhs, *lhs)
7254 } else {
7255 continue;
7256 };
7257
7258 let Some(const_term_data) = manager.get(const_term) else {
7260 continue;
7261 };
7262
7263 match &const_term_data.kind {
7264 TermKind::IntConst(n) => {
7265 if let Some(val) = n.to_i64() {
7266 let value_term = manager.mk_int(val);
7267 model.set(var_term, value_term);
7268 }
7269 }
7270 TermKind::RealConst(r) => {
7271 let value_term = manager.mk_real(*r);
7272 model.set(var_term, value_term);
7273 }
7274 TermKind::BitVecConst { value, width } => {
7275 if let Some(val) = value.to_u64() {
7276 let value_term = manager.mk_bitvec(val, *width);
7277 model.set(var_term, value_term);
7278 }
7279 }
7280 _ => {}
7281 }
7282 }
7283 }
7284
7285 for &term in &self.arith_terms {
7288 if model.get(term).is_some() {
7290 continue;
7291 }
7292
7293 if let Some(value) = self.arith.value(term) {
7294 let value_term = if *value.denom() == 1 {
7296 manager.mk_int(*value.numer())
7298 } else {
7299 manager.mk_real(value)
7301 };
7302 model.set(term, value_term);
7303 } else {
7304 let is_int = manager
7307 .get(term)
7308 .map(|t| t.sort == manager.sorts.int_sort)
7309 .unwrap_or(true);
7310
7311 let value_term = if is_int {
7312 manager.mk_int(0i64)
7313 } else {
7314 manager.mk_real(num_rational::Rational64::from_integer(0))
7315 };
7316 model.set(term, value_term);
7317 }
7318 }
7319
7320 for &term in &self.bv_terms {
7323 if model.get(term).is_some() {
7325 continue;
7326 }
7327
7328 let width = manager
7330 .get(term)
7331 .and_then(|t| manager.sorts.get(t.sort))
7332 .and_then(|s| s.bitvec_width())
7333 .unwrap_or(64);
7334
7335 if let Some(arith_value) = self.arith.value(term) {
7338 let int_value = arith_value.to_integer();
7339 let value_term = manager.mk_bitvec(int_value, width);
7340 model.set(term, value_term);
7341 } else if let Some(bv_value) = self.bv.get_value(term) {
7342 let value_term = manager.mk_bitvec(bv_value, width);
7344 model.set(term, value_term);
7345 } else {
7346 let value_term = manager.mk_bitvec(0i64, width);
7349 model.set(term, value_term);
7350 }
7351 }
7352
7353 self.model = Some(model);
7354 }
7355
7356 fn build_unsat_core_trivial_false(&mut self) {
7358 if !self.produce_unsat_cores {
7359 self.unsat_core = None;
7360 return;
7361 }
7362
7363 let mut core = UnsatCore::new();
7365
7366 for (i, &term) in self.assertions.iter().enumerate() {
7367 if term == TermId::new(1) {
7368 core.indices.push(i as u32);
7370
7371 if let Some(named) = self.named_assertions.iter().find(|na| na.index == i as u32)
7373 && let Some(ref name) = named.name
7374 {
7375 core.names.push(name.clone());
7376 }
7377 }
7378 }
7379
7380 self.unsat_core = Some(core);
7381 }
7382
7383 fn build_unsat_core(&mut self) {
7385 if !self.produce_unsat_cores {
7386 self.unsat_core = None;
7387 return;
7388 }
7389
7390 let mut core = UnsatCore::new();
7395
7396 if !self.assumption_vars.is_empty() {
7398 for na in &self.named_assertions {
7403 core.indices.push(na.index);
7404 if let Some(ref name) = na.name {
7405 core.names.push(name.clone());
7406 }
7407 }
7408 } else {
7409 for na in &self.named_assertions {
7412 core.indices.push(na.index);
7413 if let Some(ref name) = na.name {
7414 core.names.push(name.clone());
7415 }
7416 }
7417 }
7418
7419 self.unsat_core = Some(core);
7420 }
7421
7422 pub fn enable_assumption_based_cores(&mut self) {
7426 self.produce_unsat_cores = true;
7427 }
7430
7431 pub fn minimize_unsat_core(&mut self, manager: &mut TermManager) -> Option<UnsatCore> {
7434 if !self.produce_unsat_cores {
7435 return None;
7436 }
7437
7438 let core = self.unsat_core.as_ref()?;
7440 if core.is_empty() {
7441 return Some(core.clone());
7442 }
7443
7444 let mut core_assertions: Vec<_> = core
7446 .indices
7447 .iter()
7448 .map(|&idx| {
7449 let assertion = self.assertions[idx as usize];
7450 let name = self
7451 .named_assertions
7452 .iter()
7453 .find(|na| na.index == idx)
7454 .and_then(|na| na.name.clone());
7455 (idx, assertion, name)
7456 })
7457 .collect();
7458
7459 let mut i = 0;
7461 while i < core_assertions.len() {
7462 let mut temp_solver = Solver::new();
7464 temp_solver.set_logic(self.logic.as_deref().unwrap_or("ALL"));
7465
7466 for (j, &(_, assertion, _)) in core_assertions.iter().enumerate() {
7468 if i != j {
7469 temp_solver.assert(assertion, manager);
7470 }
7471 }
7472
7473 if temp_solver.check(manager) == SolverResult::Unsat {
7475 core_assertions.remove(i);
7477 } else {
7479 i += 1;
7481 }
7482 }
7483
7484 let mut minimized = UnsatCore::new();
7486 for (idx, _, name) in core_assertions {
7487 minimized.indices.push(idx);
7488 if let Some(n) = name {
7489 minimized.names.push(n);
7490 }
7491 }
7492
7493 Some(minimized)
7494 }
7495
7496 #[must_use]
7498 pub fn model(&self) -> Option<&Model> {
7499 self.model.as_ref()
7500 }
7501
7502 pub fn assert_many(&mut self, terms: &[TermId], manager: &mut TermManager) {
7505 for &term in terms {
7506 self.assert(term, manager);
7507 }
7508 }
7509
7510 #[must_use]
7512 pub fn num_assertions(&self) -> usize {
7513 self.assertions.len()
7514 }
7515
7516 #[must_use]
7518 pub fn num_variables(&self) -> usize {
7519 self.term_to_var.len()
7520 }
7521
7522 #[must_use]
7524 pub fn has_assertions(&self) -> bool {
7525 !self.assertions.is_empty()
7526 }
7527
7528 #[must_use]
7530 pub fn context_level(&self) -> usize {
7531 self.context_stack.len()
7532 }
7533
7534 pub fn push(&mut self) {
7536 self.context_stack.push(ContextState {
7537 num_assertions: self.assertions.len(),
7538 num_vars: self.var_to_term.len(),
7539 has_false_assertion: self.has_false_assertion,
7540 trail_position: self.trail.len(),
7541 });
7542 self.sat.push();
7543 self.euf.push();
7544 self.arith.push();
7545 if let Some(nlsat) = &mut self.nlsat {
7546 nlsat.push();
7547 }
7548 }
7549
7550 pub fn pop(&mut self) {
7552 if let Some(state) = self.context_stack.pop() {
7553 while self.trail.len() > state.trail_position {
7555 if let Some(op) = self.trail.pop() {
7556 match op {
7557 TrailOp::AssertionAdded { index } => {
7558 if self.assertions.len() > index {
7559 self.assertions.truncate(index);
7560 }
7561 }
7562 TrailOp::VarCreated { var: _, term } => {
7563 self.term_to_var.remove(&term);
7565 }
7566 TrailOp::ConstraintAdded { var } => {
7567 self.var_to_constraint.remove(&var);
7569 }
7570 TrailOp::FalseAssertionSet => {
7571 self.has_false_assertion = false;
7573 }
7574 TrailOp::NamedAssertionAdded { index } => {
7575 if self.named_assertions.len() > index {
7577 self.named_assertions.truncate(index);
7578 }
7579 }
7580 TrailOp::BvTermAdded { term } => {
7581 self.bv_terms.remove(&term);
7583 }
7584 TrailOp::ArithTermAdded { term } => {
7585 self.arith_terms.remove(&term);
7587 }
7588 }
7589 }
7590 }
7591
7592 self.assertions.truncate(state.num_assertions);
7594 self.var_to_term.truncate(state.num_vars);
7595 self.has_false_assertion = state.has_false_assertion;
7596
7597 self.sat.pop();
7598 self.euf.pop();
7599 self.arith.pop();
7600 if let Some(nlsat) = &mut self.nlsat {
7601 nlsat.pop();
7602 }
7603 }
7604 }
7605
7606 pub fn reset(&mut self) {
7608 self.sat.reset();
7609 self.euf.reset();
7610 self.arith.reset();
7611 self.bv.reset();
7612 self.term_to_var.clear();
7613 self.var_to_term.clear();
7614 self.var_to_constraint.clear();
7615 self.var_to_parsed_arith.clear();
7616 self.assertions.clear();
7617 self.named_assertions.clear();
7618 self.model = None;
7619 self.unsat_core = None;
7620 self.context_stack.clear();
7621 self.trail.clear();
7622 self.logic = None;
7623 self.theory_processed_up_to = 0;
7624 self.has_false_assertion = false;
7625 self.bv_terms.clear();
7626 self.arith_terms.clear();
7627 self.dt_var_constructors.clear();
7628 }
7629
7630 #[must_use]
7632 pub fn config(&self) -> &SolverConfig {
7633 &self.config
7634 }
7635
7636 pub fn set_config(&mut self, config: SolverConfig) {
7638 self.config = config;
7639 }
7640
7641 #[must_use]
7643 pub fn stats(&self) -> &oxiz_sat::SolverStats {
7644 self.sat.stats()
7645 }
7646}
7647
7648#[cfg(test)]
7649mod tests {
7650 use super::*;
7651
7652 #[test]
7653 fn test_solver_empty() {
7654 let mut solver = Solver::new();
7655 let mut manager = TermManager::new();
7656 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
7657 }
7658
7659 #[test]
7660 fn test_solver_true() {
7661 let mut solver = Solver::new();
7662 let mut manager = TermManager::new();
7663 let t = manager.mk_true();
7664 solver.assert(t, &mut manager);
7665 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
7666 }
7667
7668 #[test]
7669 fn test_solver_false() {
7670 let mut solver = Solver::new();
7671 let mut manager = TermManager::new();
7672 let f = manager.mk_false();
7673 solver.assert(f, &mut manager);
7674 assert_eq!(solver.check(&mut manager), SolverResult::Unsat);
7675 }
7676
7677 #[test]
7678 fn test_solver_push_pop() {
7679 let mut solver = Solver::new();
7680 let mut manager = TermManager::new();
7681
7682 let t = manager.mk_true();
7683 solver.assert(t, &mut manager);
7684 solver.push();
7685
7686 let f = manager.mk_false();
7687 solver.assert(f, &mut manager);
7688 assert_eq!(solver.check(&mut manager), SolverResult::Unsat);
7689
7690 solver.pop();
7691 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
7692 }
7693
7694 #[test]
7695 fn test_unsat_core_trivial() {
7696 let mut solver = Solver::new();
7697 let mut manager = TermManager::new();
7698 solver.set_produce_unsat_cores(true);
7699
7700 let t = manager.mk_true();
7701 let f = manager.mk_false();
7702
7703 solver.assert_named(t, "a1", &mut manager);
7704 solver.assert_named(f, "a2", &mut manager);
7705 solver.assert_named(t, "a3", &mut manager);
7706
7707 assert_eq!(solver.check(&mut manager), SolverResult::Unsat);
7708
7709 let core = solver.get_unsat_core();
7710 assert!(core.is_some());
7711
7712 let core = core.unwrap();
7713 assert!(!core.is_empty());
7714 assert!(core.names.contains(&"a2".to_string()));
7715 }
7716
7717 #[test]
7718 fn test_unsat_core_not_produced_when_sat() {
7719 let mut solver = Solver::new();
7720 let mut manager = TermManager::new();
7721 solver.set_produce_unsat_cores(true);
7722
7723 let t = manager.mk_true();
7724 solver.assert_named(t, "a1", &mut manager);
7725 solver.assert_named(t, "a2", &mut manager);
7726
7727 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
7728 assert!(solver.get_unsat_core().is_none());
7729 }
7730
7731 #[test]
7732 fn test_unsat_core_disabled() {
7733 let mut solver = Solver::new();
7734 let mut manager = TermManager::new();
7735 let f = manager.mk_false();
7738 solver.assert(f, &mut manager);
7739 assert_eq!(solver.check(&mut manager), SolverResult::Unsat);
7740
7741 assert!(solver.get_unsat_core().is_none());
7743 }
7744
7745 #[test]
7746 fn test_boolean_encoding_and() {
7747 let mut solver = Solver::new();
7748 let mut manager = TermManager::new();
7749
7750 let p = manager.mk_var("p", manager.sorts.bool_sort);
7752 let q = manager.mk_var("q", manager.sorts.bool_sort);
7753 let and = manager.mk_and(vec![p, q]);
7754
7755 solver.assert(and, &mut manager);
7756 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
7757
7758 let model = solver.model().expect("Should have model");
7760 assert!(model.get(p).is_some());
7761 assert!(model.get(q).is_some());
7762 }
7763
7764 #[test]
7765 fn test_boolean_encoding_or() {
7766 let mut solver = Solver::new();
7767 let mut manager = TermManager::new();
7768
7769 let p = manager.mk_var("p", manager.sorts.bool_sort);
7771 let q = manager.mk_var("q", manager.sorts.bool_sort);
7772 let or = manager.mk_or(vec![p, q]);
7773 let not_p = manager.mk_not(p);
7774
7775 solver.assert(or, &mut manager);
7776 solver.assert(not_p, &mut manager);
7777 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
7778 }
7779
7780 #[test]
7781 fn test_boolean_encoding_implies() {
7782 let mut solver = Solver::new();
7783 let mut manager = TermManager::new();
7784
7785 let p = manager.mk_var("p", manager.sorts.bool_sort);
7787 let q = manager.mk_var("q", manager.sorts.bool_sort);
7788 let implies = manager.mk_implies(p, q);
7789 let not_q = manager.mk_not(q);
7790
7791 solver.assert(implies, &mut manager);
7792 solver.assert(p, &mut manager);
7793 solver.assert(not_q, &mut manager);
7794 assert_eq!(solver.check(&mut manager), SolverResult::Unsat);
7795 }
7796
7797 #[test]
7798 fn test_boolean_encoding_distinct() {
7799 let mut solver = Solver::new();
7800 let mut manager = TermManager::new();
7801
7802 let p = manager.mk_var("p", manager.sorts.bool_sort);
7804 let q = manager.mk_var("q", manager.sorts.bool_sort);
7805 let r = manager.mk_var("r", manager.sorts.bool_sort);
7806 let distinct = manager.mk_distinct(vec![p, q, r]);
7807
7808 solver.assert(distinct, &mut manager);
7809 solver.assert(p, &mut manager);
7810 solver.assert(q, &mut manager);
7811 assert_eq!(solver.check(&mut manager), SolverResult::Unsat);
7812 }
7813
7814 #[test]
7815 fn test_model_evaluation_bool() {
7816 let mut solver = Solver::new();
7817 let mut manager = TermManager::new();
7818
7819 let p = manager.mk_var("p", manager.sorts.bool_sort);
7820 let q = manager.mk_var("q", manager.sorts.bool_sort);
7821
7822 solver.assert(p, &mut manager);
7824 solver.assert(manager.mk_not(q), &mut manager);
7825 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
7826
7827 let model = solver.model().expect("Should have model");
7828
7829 let p_val = model.eval(p, &mut manager);
7831 assert_eq!(p_val, manager.mk_true());
7832
7833 let q_val = model.eval(q, &mut manager);
7835 assert_eq!(q_val, manager.mk_false());
7836
7837 let and_term = manager.mk_and(vec![p, q]);
7839 let and_val = model.eval(and_term, &mut manager);
7840 assert_eq!(and_val, manager.mk_false());
7841
7842 let or_term = manager.mk_or(vec![p, q]);
7844 let or_val = model.eval(or_term, &mut manager);
7845 assert_eq!(or_val, manager.mk_true());
7846 }
7847
7848 #[test]
7849 fn test_model_evaluation_ite() {
7850 let mut solver = Solver::new();
7851 let mut manager = TermManager::new();
7852
7853 let p = manager.mk_var("p", manager.sorts.bool_sort);
7854 let q = manager.mk_var("q", manager.sorts.bool_sort);
7855 let r = manager.mk_var("r", manager.sorts.bool_sort);
7856
7857 solver.assert(p, &mut manager);
7859 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
7860
7861 let model = solver.model().expect("Should have model");
7862
7863 let ite_term = manager.mk_ite(p, q, r);
7865 let ite_val = model.eval(ite_term, &mut manager);
7866 let q_val = model.eval(q, &mut manager);
7868 assert_eq!(ite_val, q_val);
7869 }
7870
7871 #[test]
7872 fn test_model_evaluation_implies() {
7873 let mut solver = Solver::new();
7874 let mut manager = TermManager::new();
7875
7876 let p = manager.mk_var("p", manager.sorts.bool_sort);
7877 let q = manager.mk_var("q", manager.sorts.bool_sort);
7878
7879 solver.assert(manager.mk_not(p), &mut manager);
7881 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
7882
7883 let model = solver.model().expect("Should have model");
7884
7885 let implies_term = manager.mk_implies(p, q);
7887 let implies_val = model.eval(implies_term, &mut manager);
7888 assert_eq!(implies_val, manager.mk_true());
7889 }
7890
7891 #[test]
7897 #[ignore = "Known BV model extraction issue - solver returns SAT but model extraction returns 0"]
7898 fn test_bv_comparison_model_generation() {
7899 let mut solver = Solver::new();
7901 let mut manager = TermManager::new();
7902
7903 solver.set_logic("QF_BV");
7904
7905 let bv8_sort = manager.sorts.bitvec(8);
7907 let x = manager.mk_var("x", bv8_sort);
7908
7909 let five = manager.mk_bitvec(5i64, 8);
7911 let ten = manager.mk_bitvec(10i64, 8);
7912
7913 let lt1 = manager.mk_bv_ult(five, x);
7915 solver.assert(lt1, &mut manager);
7916
7917 let lt2 = manager.mk_bv_ult(x, ten);
7919 solver.assert(lt2, &mut manager);
7920
7921 let result = solver.check(&mut manager);
7922 assert_eq!(result, SolverResult::Sat);
7923
7924 let model = solver.model().expect("Should have model");
7926
7927 if let Some(x_value_id) = model.get(x)
7929 && let Some(x_term) = manager.get(x_value_id)
7930 && let TermKind::BitVecConst { value, .. } = &x_term.kind
7931 {
7932 let x_val = value.to_u64().unwrap_or(0);
7933 assert!(
7935 (6..=9).contains(&x_val),
7936 "Expected x in [6,9], got {}",
7937 x_val
7938 );
7939 }
7940 }
7941
7942 #[test]
7943 fn test_arithmetic_model_generation() {
7944 use num_bigint::BigInt;
7945
7946 let mut solver = Solver::new();
7947 let mut manager = TermManager::new();
7948
7949 let x = manager.mk_var("x", manager.sorts.int_sort);
7951 let y = manager.mk_var("y", manager.sorts.int_sort);
7952
7953 let ten = manager.mk_int(BigInt::from(10));
7955 let zero = manager.mk_int(BigInt::from(0));
7956 let sum = manager.mk_add(vec![x, y]);
7957
7958 let eq = manager.mk_eq(sum, ten);
7959 let x_ge_0 = manager.mk_ge(x, zero);
7960 let y_ge_0 = manager.mk_ge(y, zero);
7961
7962 solver.assert(eq, &mut manager);
7963 solver.assert(x_ge_0, &mut manager);
7964 solver.assert(y_ge_0, &mut manager);
7965
7966 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
7967
7968 let model = solver.model();
7970 assert!(model.is_some(), "Should have a model for SAT result");
7971 }
7972
7973 #[test]
7974 fn test_model_pretty_print() {
7975 let mut solver = Solver::new();
7976 let mut manager = TermManager::new();
7977
7978 let p = manager.mk_var("p", manager.sorts.bool_sort);
7979 let q = manager.mk_var("q", manager.sorts.bool_sort);
7980
7981 solver.assert(p, &mut manager);
7982 solver.assert(manager.mk_not(q), &mut manager);
7983 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
7984
7985 let model = solver.model().expect("Should have model");
7986 let pretty = model.pretty_print(&manager);
7987
7988 assert!(pretty.contains("(model"));
7990 assert!(pretty.contains("define-fun"));
7991 assert!(pretty.contains("p") || pretty.contains("q"));
7993 }
7994
7995 #[test]
7996 fn test_trail_based_undo_assertions() {
7997 let mut solver = Solver::new();
7998 let mut manager = TermManager::new();
7999
8000 let p = manager.mk_var("p", manager.sorts.bool_sort);
8001 let q = manager.mk_var("q", manager.sorts.bool_sort);
8002
8003 assert_eq!(solver.assertions.len(), 0);
8005 assert_eq!(solver.trail.len(), 0);
8006
8007 solver.assert(p, &mut manager);
8009 assert_eq!(solver.assertions.len(), 1);
8010 assert!(!solver.trail.is_empty());
8011
8012 solver.push();
8014 let trail_len_after_push = solver.trail.len();
8015 solver.assert(q, &mut manager);
8016 assert_eq!(solver.assertions.len(), 2);
8017 assert!(solver.trail.len() > trail_len_after_push);
8018
8019 solver.pop();
8021 assert_eq!(solver.assertions.len(), 1);
8022 assert_eq!(solver.assertions[0], p);
8023 }
8024
8025 #[test]
8026 fn test_trail_based_undo_variables() {
8027 let mut solver = Solver::new();
8028 let mut manager = TermManager::new();
8029
8030 let p = manager.mk_var("p", manager.sorts.bool_sort);
8031 let q = manager.mk_var("q", manager.sorts.bool_sort);
8032
8033 solver.assert(p, &mut manager);
8035 let initial_var_count = solver.term_to_var.len();
8036
8037 solver.push();
8039 solver.assert(q, &mut manager);
8040 assert!(solver.term_to_var.len() >= initial_var_count);
8041
8042 solver.pop();
8044 assert_eq!(solver.assertions.len(), 1);
8046 }
8047
8048 #[test]
8049 fn test_trail_based_undo_constraints() {
8050 use num_bigint::BigInt;
8051
8052 let mut solver = Solver::new();
8053 let mut manager = TermManager::new();
8054
8055 let x = manager.mk_var("x", manager.sorts.int_sort);
8056 let zero = manager.mk_int(BigInt::from(0));
8057
8058 let c1 = manager.mk_ge(x, zero);
8060 solver.assert(c1, &mut manager);
8061 let initial_constraint_count = solver.var_to_constraint.len();
8062
8063 solver.push();
8065 let ten = manager.mk_int(BigInt::from(10));
8066 let c2 = manager.mk_le(x, ten);
8067 solver.assert(c2, &mut manager);
8068 assert!(solver.var_to_constraint.len() >= initial_constraint_count);
8069
8070 solver.pop();
8072 assert_eq!(solver.var_to_constraint.len(), initial_constraint_count);
8073 }
8074
8075 #[test]
8076 fn test_trail_based_undo_false_assertion() {
8077 let mut solver = Solver::new();
8078 let mut manager = TermManager::new();
8079
8080 assert!(!solver.has_false_assertion);
8081
8082 solver.push();
8083 solver.assert(manager.mk_false(), &mut manager);
8084 assert!(solver.has_false_assertion);
8085
8086 solver.pop();
8087 assert!(!solver.has_false_assertion);
8088 }
8089
8090 #[test]
8091 fn test_trail_based_undo_named_assertions() {
8092 let mut solver = Solver::new();
8093 let mut manager = TermManager::new();
8094 solver.set_produce_unsat_cores(true);
8095
8096 let p = manager.mk_var("p", manager.sorts.bool_sort);
8097
8098 solver.assert_named(p, "assertion1", &mut manager);
8099 assert_eq!(solver.named_assertions.len(), 1);
8100
8101 solver.push();
8102 let q = manager.mk_var("q", manager.sorts.bool_sort);
8103 solver.assert_named(q, "assertion2", &mut manager);
8104 assert_eq!(solver.named_assertions.len(), 2);
8105
8106 solver.pop();
8107 assert_eq!(solver.named_assertions.len(), 1);
8108 assert_eq!(
8109 solver.named_assertions[0].name,
8110 Some("assertion1".to_string())
8111 );
8112 }
8113
8114 #[test]
8115 fn test_trail_based_undo_nested_push_pop() {
8116 let mut solver = Solver::new();
8117 let mut manager = TermManager::new();
8118
8119 let p = manager.mk_var("p", manager.sorts.bool_sort);
8120 solver.assert(p, &mut manager);
8121
8122 solver.push();
8124 let q = manager.mk_var("q", manager.sorts.bool_sort);
8125 solver.assert(q, &mut manager);
8126 assert_eq!(solver.assertions.len(), 2);
8127
8128 solver.push();
8130 let r = manager.mk_var("r", manager.sorts.bool_sort);
8131 solver.assert(r, &mut manager);
8132 assert_eq!(solver.assertions.len(), 3);
8133
8134 solver.pop();
8136 assert_eq!(solver.assertions.len(), 2);
8137
8138 solver.pop();
8140 assert_eq!(solver.assertions.len(), 1);
8141 assert_eq!(solver.assertions[0], p);
8142 }
8143
8144 #[test]
8145 fn test_config_presets() {
8146 let _fast = SolverConfig::fast();
8148 let _balanced = SolverConfig::balanced();
8149 let _thorough = SolverConfig::thorough();
8150 let _minimal = SolverConfig::minimal();
8151 }
8152
8153 #[test]
8154 fn test_config_fast_characteristics() {
8155 let config = SolverConfig::fast();
8156
8157 assert!(!config.enable_variable_elimination);
8159 assert!(!config.enable_blocked_clause_elimination);
8160 assert!(!config.enable_symmetry_breaking);
8161 assert!(!config.enable_inprocessing);
8162 assert!(!config.enable_clause_subsumption);
8163
8164 assert!(config.enable_clause_minimization);
8166 assert!(config.simplify);
8167
8168 assert_eq!(config.restart_strategy, RestartStrategy::Geometric);
8170 }
8171
8172 #[test]
8173 fn test_config_balanced_characteristics() {
8174 let config = SolverConfig::balanced();
8175
8176 assert!(config.enable_variable_elimination);
8178 assert!(config.enable_blocked_clause_elimination);
8179 assert!(config.enable_inprocessing);
8180 assert!(config.enable_clause_minimization);
8181 assert!(config.enable_clause_subsumption);
8182 assert!(config.simplify);
8183
8184 assert!(!config.enable_symmetry_breaking);
8186
8187 assert_eq!(config.restart_strategy, RestartStrategy::Glucose);
8189
8190 assert_eq!(config.variable_elimination_limit, 1000);
8192 assert_eq!(config.inprocessing_interval, 10000);
8193 }
8194
8195 #[test]
8196 fn test_config_thorough_characteristics() {
8197 let config = SolverConfig::thorough();
8198
8199 assert!(config.enable_variable_elimination);
8201 assert!(config.enable_blocked_clause_elimination);
8202 assert!(config.enable_symmetry_breaking); assert!(config.enable_inprocessing);
8204 assert!(config.enable_clause_minimization);
8205 assert!(config.enable_clause_subsumption);
8206 assert!(config.simplify);
8207
8208 assert_eq!(config.variable_elimination_limit, 5000);
8210 assert_eq!(config.inprocessing_interval, 5000);
8211 }
8212
8213 #[test]
8214 fn test_config_minimal_characteristics() {
8215 let config = SolverConfig::minimal();
8216
8217 assert!(!config.simplify);
8219 assert!(!config.enable_variable_elimination);
8220 assert!(!config.enable_blocked_clause_elimination);
8221 assert!(!config.enable_symmetry_breaking);
8222 assert!(!config.enable_inprocessing);
8223 assert!(!config.enable_clause_minimization);
8224 assert!(!config.enable_clause_subsumption);
8225
8226 assert_eq!(config.theory_mode, TheoryMode::Lazy);
8228
8229 assert_eq!(config.num_threads, 1);
8231 }
8232
8233 #[test]
8234 fn test_config_builder_pattern() {
8235 let config = SolverConfig::fast()
8237 .with_proof()
8238 .with_timeout(5000)
8239 .with_max_conflicts(1000)
8240 .with_max_decisions(2000)
8241 .with_parallel(8)
8242 .with_restart_strategy(RestartStrategy::Luby)
8243 .with_theory_mode(TheoryMode::Lazy);
8244
8245 assert!(config.proof);
8246 assert_eq!(config.timeout_ms, 5000);
8247 assert_eq!(config.max_conflicts, 1000);
8248 assert_eq!(config.max_decisions, 2000);
8249 assert!(config.parallel);
8250 assert_eq!(config.num_threads, 8);
8251 assert_eq!(config.restart_strategy, RestartStrategy::Luby);
8252 assert_eq!(config.theory_mode, TheoryMode::Lazy);
8253 }
8254
8255 #[test]
8256 fn test_solver_with_different_configs() {
8257 let mut manager = TermManager::new();
8258
8259 let mut solver_fast = Solver::with_config(SolverConfig::fast());
8261 let mut solver_balanced = Solver::with_config(SolverConfig::balanced());
8262 let mut solver_thorough = Solver::with_config(SolverConfig::thorough());
8263 let mut solver_minimal = Solver::with_config(SolverConfig::minimal());
8264
8265 let t = manager.mk_true();
8267 solver_fast.assert(t, &mut manager);
8268 solver_balanced.assert(t, &mut manager);
8269 solver_thorough.assert(t, &mut manager);
8270 solver_minimal.assert(t, &mut manager);
8271
8272 assert_eq!(solver_fast.check(&mut manager), SolverResult::Sat);
8273 assert_eq!(solver_balanced.check(&mut manager), SolverResult::Sat);
8274 assert_eq!(solver_thorough.check(&mut manager), SolverResult::Sat);
8275 assert_eq!(solver_minimal.check(&mut manager), SolverResult::Sat);
8276 }
8277
8278 #[test]
8279 fn test_config_default_is_balanced() {
8280 let default = SolverConfig::default();
8281 let balanced = SolverConfig::balanced();
8282
8283 assert_eq!(
8285 default.enable_variable_elimination,
8286 balanced.enable_variable_elimination
8287 );
8288 assert_eq!(
8289 default.enable_clause_minimization,
8290 balanced.enable_clause_minimization
8291 );
8292 assert_eq!(
8293 default.enable_symmetry_breaking,
8294 balanced.enable_symmetry_breaking
8295 );
8296 assert_eq!(default.restart_strategy, balanced.restart_strategy);
8297 }
8298
8299 #[test]
8300 fn test_theory_combination_arith_solver() {
8301 use oxiz_theories::arithmetic::ArithSolver;
8302 use oxiz_theories::{EqualityNotification, TheoryCombination};
8303
8304 let mut arith = ArithSolver::lra();
8305 let mut manager = TermManager::new();
8306
8307 let x = manager.mk_var("x", manager.sorts.int_sort);
8309 let y = manager.mk_var("y", manager.sorts.int_sort);
8310
8311 let _x_var = arith.intern(x);
8313 let _y_var = arith.intern(y);
8314
8315 let eq_notification = EqualityNotification {
8317 lhs: x,
8318 rhs: y,
8319 reason: None,
8320 };
8321
8322 let accepted = arith.notify_equality(eq_notification);
8323 assert!(
8324 accepted,
8325 "ArithSolver should accept equality notification for known terms"
8326 );
8327
8328 assert!(
8330 arith.is_relevant(x),
8331 "x should be relevant to arithmetic solver"
8332 );
8333 assert!(
8334 arith.is_relevant(y),
8335 "y should be relevant to arithmetic solver"
8336 );
8337
8338 let z = manager.mk_var("z", manager.sorts.int_sort);
8340 assert!(
8341 !arith.is_relevant(z),
8342 "z should not be relevant (not interned)"
8343 );
8344
8345 let eq_unknown = EqualityNotification {
8347 lhs: x,
8348 rhs: z,
8349 reason: None,
8350 };
8351 let accepted_unknown = arith.notify_equality(eq_unknown);
8352 assert!(
8353 !accepted_unknown,
8354 "ArithSolver should reject equality with unknown term"
8355 );
8356 }
8357
8358 #[test]
8359 fn test_theory_combination_get_shared_equalities() {
8360 use oxiz_theories::TheoryCombination;
8361 use oxiz_theories::arithmetic::ArithSolver;
8362
8363 let arith = ArithSolver::lra();
8364
8365 let shared = arith.get_shared_equalities();
8367 assert!(
8368 shared.is_empty(),
8369 "ArithSolver should return empty shared equalities (placeholder)"
8370 );
8371 }
8372
8373 #[test]
8374 fn test_equality_notification_fields() {
8375 use oxiz_theories::EqualityNotification;
8376
8377 let mut manager = TermManager::new();
8378 let x = manager.mk_var("x", manager.sorts.int_sort);
8379 let y = manager.mk_var("y", manager.sorts.int_sort);
8380
8381 let eq1 = EqualityNotification {
8383 lhs: x,
8384 rhs: y,
8385 reason: Some(x),
8386 };
8387 assert_eq!(eq1.lhs, x);
8388 assert_eq!(eq1.rhs, y);
8389 assert_eq!(eq1.reason, Some(x));
8390
8391 let eq2 = EqualityNotification {
8393 lhs: x,
8394 rhs: y,
8395 reason: None,
8396 };
8397 assert_eq!(eq2.reason, None);
8398
8399 let eq3 = eq1;
8401 assert_eq!(eq3.lhs, eq1.lhs);
8402 assert_eq!(eq3.rhs, eq1.rhs);
8403 }
8404
8405 #[test]
8406 fn test_assert_many() {
8407 let mut solver = Solver::new();
8408 let mut manager = TermManager::new();
8409
8410 let p = manager.mk_var("p", manager.sorts.bool_sort);
8411 let q = manager.mk_var("q", manager.sorts.bool_sort);
8412 let r = manager.mk_var("r", manager.sorts.bool_sort);
8413
8414 solver.assert_many(&[p, q, r], &mut manager);
8416
8417 assert_eq!(solver.num_assertions(), 3);
8418 assert_eq!(solver.check(&mut manager), SolverResult::Sat);
8419 }
8420
8421 #[test]
8422 fn test_num_assertions_and_variables() {
8423 let mut solver = Solver::new();
8424 let mut manager = TermManager::new();
8425
8426 assert_eq!(solver.num_assertions(), 0);
8427 assert!(!solver.has_assertions());
8428
8429 let p = manager.mk_var("p", manager.sorts.bool_sort);
8430 let q = manager.mk_var("q", manager.sorts.bool_sort);
8431
8432 solver.assert(p, &mut manager);
8433 assert_eq!(solver.num_assertions(), 1);
8434 assert!(solver.has_assertions());
8435
8436 solver.assert(q, &mut manager);
8437 assert_eq!(solver.num_assertions(), 2);
8438
8439 assert!(solver.num_variables() > 0);
8441 }
8442
8443 #[test]
8444 fn test_context_level() {
8445 let mut solver = Solver::new();
8446
8447 assert_eq!(solver.context_level(), 0);
8448
8449 solver.push();
8450 assert_eq!(solver.context_level(), 1);
8451
8452 solver.push();
8453 assert_eq!(solver.context_level(), 2);
8454
8455 solver.pop();
8456 assert_eq!(solver.context_level(), 1);
8457
8458 solver.pop();
8459 assert_eq!(solver.context_level(), 0);
8460 }
8461
8462 #[test]
8465 fn test_quantifier_basic_forall() {
8466 let mut solver = Solver::new();
8467 let mut manager = TermManager::new();
8468 let bool_sort = manager.sorts.bool_sort;
8469
8470 let x = manager.mk_var("x", bool_sort);
8473 let p_x = manager.mk_apply("P", [x], bool_sort);
8474 let forall = manager.mk_forall([("x", bool_sort)], p_x);
8475
8476 solver.assert(forall, &mut manager);
8477
8478 let result = solver.check(&mut manager);
8480 assert!(
8482 result == SolverResult::Sat || result == SolverResult::Unknown,
8483 "Basic forall should not be unsat"
8484 );
8485 }
8486
8487 #[test]
8488 fn test_quantifier_basic_exists() {
8489 let mut solver = Solver::new();
8490 let mut manager = TermManager::new();
8491 let bool_sort = manager.sorts.bool_sort;
8492
8493 let x = manager.mk_var("x", bool_sort);
8495 let p_x = manager.mk_apply("P", [x], bool_sort);
8496 let exists = manager.mk_exists([("x", bool_sort)], p_x);
8497
8498 solver.assert(exists, &mut manager);
8499
8500 let result = solver.check(&mut manager);
8501 assert!(
8502 result == SolverResult::Sat || result == SolverResult::Unknown,
8503 "Basic exists should not be unsat"
8504 );
8505 }
8506
8507 #[test]
8508 fn test_quantifier_with_ground_terms() {
8509 let mut solver = Solver::new();
8510 let mut manager = TermManager::new();
8511 let int_sort = manager.sorts.int_sort;
8512 let bool_sort = manager.sorts.bool_sort;
8513
8514 let zero = manager.mk_int(0);
8516 let one = manager.mk_int(1);
8517
8518 let p_0 = manager.mk_apply("P", [zero], bool_sort);
8520 let p_1 = manager.mk_apply("P", [one], bool_sort);
8521 solver.assert(p_0, &mut manager);
8522 solver.assert(p_1, &mut manager);
8523
8524 let x = manager.mk_var("x", int_sort);
8526 let p_x = manager.mk_apply("P", [x], bool_sort);
8527 let forall = manager.mk_forall([("x", int_sort)], p_x);
8528 solver.assert(forall, &mut manager);
8529
8530 let result = solver.check(&mut manager);
8531 assert!(
8533 result == SolverResult::Sat || result == SolverResult::Unknown,
8534 "Quantifier with matching ground terms should be satisfiable"
8535 );
8536 }
8537
8538 #[test]
8539 fn test_quantifier_instantiation() {
8540 let mut solver = Solver::new();
8541 let mut manager = TermManager::new();
8542 let int_sort = manager.sorts.int_sort;
8543 let bool_sort = manager.sorts.bool_sort;
8544
8545 let c = manager.mk_apply("c", [], int_sort);
8547
8548 let x = manager.mk_var("x", int_sort);
8550 let f_x = manager.mk_apply("f", [x], int_sort);
8551 let zero = manager.mk_int(0);
8552 let f_x_gt_0 = manager.mk_gt(f_x, zero);
8553 let forall = manager.mk_forall([("x", int_sort)], f_x_gt_0);
8554 solver.assert(forall, &mut manager);
8555
8556 let f_c = manager.mk_apply("f", [c], int_sort);
8558 let f_c_exists = manager.mk_apply("exists_f_c", [f_c], bool_sort);
8559 solver.assert(f_c_exists, &mut manager);
8560
8561 let result = solver.check(&mut manager);
8562 assert!(
8564 result == SolverResult::Sat || result == SolverResult::Unknown,
8565 "Quantifier instantiation test"
8566 );
8567 }
8568
8569 #[test]
8570 fn test_quantifier_mbqi_solver_integration() {
8571 use crate::mbqi::MBQIIntegration;
8572
8573 let mut mbqi = MBQIIntegration::new();
8574 let mut manager = TermManager::new();
8575 let int_sort = manager.sorts.int_sort;
8576
8577 let x = manager.mk_var("x", int_sort);
8579 let zero = manager.mk_int(0);
8580 let x_gt_0 = manager.mk_gt(x, zero);
8581 let forall = manager.mk_forall([("x", int_sort)], x_gt_0);
8582
8583 mbqi.add_quantifier(forall, &manager);
8585
8586 let one = manager.mk_int(1);
8588 let two = manager.mk_int(2);
8589 mbqi.add_candidate(one, int_sort);
8590 mbqi.add_candidate(two, int_sort);
8591
8592 assert!(mbqi.is_enabled(), "MBQI should be enabled by default");
8594 }
8595
8596 #[test]
8597 fn test_quantifier_pattern_matching() {
8598 let mut solver = Solver::new();
8599 let mut manager = TermManager::new();
8600 let int_sort = manager.sorts.int_sort;
8601
8602 let x = manager.mk_var("x", int_sort);
8604 let f_x = manager.mk_apply("f", [x], int_sort);
8605 let g_x = manager.mk_apply("g", [x], int_sort);
8606 let body = manager.mk_eq(f_x, g_x);
8607
8608 let pattern: smallvec::SmallVec<[_; 2]> = smallvec::smallvec![f_x];
8610 let patterns: smallvec::SmallVec<[_; 2]> = smallvec::smallvec![pattern];
8611
8612 let forall = manager.mk_forall_with_patterns([("x", int_sort)], body, patterns);
8613 solver.assert(forall, &mut manager);
8614
8615 let c = manager.mk_apply("c", [], int_sort);
8617 let f_c = manager.mk_apply("f", [c], int_sort);
8618 let f_c_eq_c = manager.mk_eq(f_c, c);
8619 solver.assert(f_c_eq_c, &mut manager);
8620
8621 let result = solver.check(&mut manager);
8622 assert!(
8623 result == SolverResult::Sat || result == SolverResult::Unknown,
8624 "Pattern matching should allow instantiation"
8625 );
8626 }
8627
8628 #[test]
8629 fn test_quantifier_multiple() {
8630 let mut solver = Solver::new();
8631 let mut manager = TermManager::new();
8632 let int_sort = manager.sorts.int_sort;
8633
8634 let x = manager.mk_var("x", int_sort);
8636 let y = manager.mk_var("y", int_sort);
8637 let x_plus_y = manager.mk_add([x, y]);
8638 let y_plus_x = manager.mk_add([y, x]);
8639 let commutative = manager.mk_eq(x_plus_y, y_plus_x);
8640
8641 let inner_forall = manager.mk_forall([("y", int_sort)], commutative);
8642 let outer_forall = manager.mk_forall([("x", int_sort)], inner_forall);
8643
8644 solver.assert(outer_forall, &mut manager);
8645
8646 let result = solver.check(&mut manager);
8647 assert!(
8648 result == SolverResult::Sat || result == SolverResult::Unknown,
8649 "Nested forall should be handled"
8650 );
8651 }
8652
8653 #[test]
8654 fn test_quantifier_with_model() {
8655 let mut solver = Solver::new();
8656 let mut manager = TermManager::new();
8657 let bool_sort = manager.sorts.bool_sort;
8658
8659 let p = manager.mk_var("p", bool_sort);
8661 solver.assert(p, &mut manager);
8662
8663 let x = manager.mk_var("x", bool_sort);
8665 let not_x = manager.mk_not(x);
8666 let x_or_not_x = manager.mk_or([x, not_x]);
8667 let tautology = manager.mk_forall([("x", bool_sort)], x_or_not_x);
8668 solver.assert(tautology, &mut manager);
8669
8670 let result = solver.check(&mut manager);
8671 assert!(
8672 result == SolverResult::Sat || result == SolverResult::Unknown,
8673 "Tautology in quantifier should be SAT or Unknown (MBQI in progress)"
8674 );
8675
8676 if result == SolverResult::Sat
8678 && let Some(model) = solver.model()
8679 {
8680 assert!(model.size() > 0, "Model should have assignments");
8681 }
8682 }
8683
8684 #[test]
8685 fn test_quantifier_push_pop() {
8686 let mut solver = Solver::new();
8687 let mut manager = TermManager::new();
8688 let int_sort = manager.sorts.int_sort;
8689
8690 let x = manager.mk_var("x", int_sort);
8692 let zero = manager.mk_int(0);
8693 let x_gt_0 = manager.mk_gt(x, zero);
8694 let forall = manager.mk_forall([("x", int_sort)], x_gt_0);
8695
8696 solver.push();
8697 solver.assert(forall, &mut manager);
8698
8699 let result1 = solver.check(&mut manager);
8700 assert!(
8703 result1 == SolverResult::Unsat || result1 == SolverResult::Unknown,
8704 "forall x. x > 0 should be Unsat or Unknown, got {:?}",
8705 result1
8706 );
8707
8708 solver.pop();
8709
8710 let result2 = solver.check(&mut manager);
8712 assert_eq!(
8713 result2,
8714 SolverResult::Sat,
8715 "After pop, should be trivially SAT"
8716 );
8717 }
8718
8719 #[test]
8725 fn test_integer_contradiction_unsat() {
8726 use num_bigint::BigInt;
8727
8728 let mut solver = Solver::new();
8729 let mut manager = TermManager::new();
8730
8731 let x = manager.mk_var("x", manager.sorts.int_sort);
8733 let zero = manager.mk_int(BigInt::from(0));
8734
8735 let x_ge_0 = manager.mk_ge(x, zero);
8737 solver.assert(x_ge_0, &mut manager);
8738
8739 let x_lt_0 = manager.mk_lt(x, zero);
8741 solver.assert(x_lt_0, &mut manager);
8742
8743 let result = solver.check(&mut manager);
8745 assert_eq!(
8746 result,
8747 SolverResult::Unsat,
8748 "x >= 0 AND x < 0 should be UNSAT"
8749 );
8750 }
8751
8752 #[test]
8761 fn test_lia_empty_interval_unsat() {
8762 use num_bigint::BigInt;
8763
8764 let mut solver = Solver::new();
8765 let mut manager = TermManager::new();
8766
8767 solver.set_logic("QF_LIA");
8768
8769 let x = manager.mk_var("x", manager.sorts.int_sort);
8771 let five = manager.mk_int(BigInt::from(5));
8772 let six = manager.mk_int(BigInt::from(6));
8773
8774 let x_gt_5 = manager.mk_gt(x, five);
8776 solver.assert(x_gt_5, &mut manager);
8777
8778 let x_lt_6 = manager.mk_lt(x, six);
8780 solver.assert(x_lt_6, &mut manager);
8781
8782 let result = solver.check(&mut manager);
8784 assert_eq!(
8785 result,
8786 SolverResult::Unsat,
8787 "x > 5 AND x < 6 should be UNSAT for integers (no integer in open interval)"
8788 );
8789 }
8790}