1#[allow(unused_imports)]
4use crate::prelude::*;
5use num_rational::Rational64;
6use num_traits::{One, ToPrimitive, Zero};
7use oxiz_core::ast::{TermId, TermKind, TermManager};
8use oxiz_sat::{Lit, Var};
9use smallvec::SmallVec;
10
11use super::Solver;
12use super::trail::TrailOp;
13use super::types::{
14 ArithConstraintType, Constraint, NamedAssertion, ParsedArithConstraint, Polarity, UnsatCore,
15};
16
17impl Solver {
18 pub(super) fn get_or_create_var(&mut self, term: TermId) -> Var {
19 if let Some(&var) = self.term_to_var.get(&term) {
20 return var;
21 }
22
23 let var = self.sat.new_var();
24 self.term_to_var.insert(term, var);
25 self.trail.push(TrailOp::VarCreated { var, term });
26
27 while self.var_to_term.len() <= var.index() {
28 self.var_to_term.push(TermId::new(0));
29 }
30 self.var_to_term[var.index()] = term;
31 var
32 }
33
34 pub(super) fn track_theory_vars(&mut self, term_id: TermId, manager: &TermManager) {
41 let Some(term) = manager.get(term_id) else {
42 return;
43 };
44
45 match &term.kind {
46 TermKind::Var(_) => {
47 let is_int = term.sort == manager.sorts.int_sort;
49 let is_real = term.sort == manager.sorts.real_sort;
50
51 if is_int || is_real {
52 if !self.arith_terms.contains(&term_id) {
53 self.arith_terms.insert(term_id);
54 self.trail.push(TrailOp::ArithTermAdded { term: term_id });
55 self.arith.intern(term_id);
56 }
57 } else if let Some(sort) = manager.sorts.get(term.sort)
58 && sort.is_bitvec()
59 && !self.bv_terms.contains(&term_id)
60 {
61 self.bv_terms.insert(term_id);
62 self.trail.push(TrailOp::BvTermAdded { term: term_id });
63 if let Some(width) = sort.bitvec_width() {
64 self.bv.new_bv(term_id, width);
65 }
66 self.arith.intern(term_id);
69 }
70 }
71 TermKind::Add(args)
74 | TermKind::Mul(args)
75 | TermKind::And(args)
76 | TermKind::Or(args) => {
77 if self.tracked_compound_terms.contains(&term_id) {
78 return;
79 }
80 self.tracked_compound_terms.insert(term_id);
81 let args_vec: SmallVec<[TermId; 8]> = args.iter().copied().collect();
83 for arg in args_vec {
84 self.track_theory_vars(arg, manager);
85 }
86 }
87 TermKind::Sub(lhs, rhs)
88 | TermKind::Eq(lhs, rhs)
89 | TermKind::Lt(lhs, rhs)
90 | TermKind::Le(lhs, rhs)
91 | TermKind::Gt(lhs, rhs)
92 | TermKind::Ge(lhs, rhs)
93 | TermKind::BvAdd(lhs, rhs)
94 | TermKind::BvSub(lhs, rhs)
95 | TermKind::BvMul(lhs, rhs)
96 | TermKind::BvAnd(lhs, rhs)
97 | TermKind::BvOr(lhs, rhs)
98 | TermKind::BvXor(lhs, rhs)
99 | TermKind::BvUlt(lhs, rhs)
100 | TermKind::BvUle(lhs, rhs)
101 | TermKind::BvSlt(lhs, rhs)
102 | TermKind::BvSle(lhs, rhs) => {
103 if self.tracked_compound_terms.contains(&term_id) {
104 return;
105 }
106 self.tracked_compound_terms.insert(term_id);
107 let (l, r) = (*lhs, *rhs);
108 self.track_theory_vars(l, manager);
109 self.track_theory_vars(r, manager);
110 }
111 TermKind::BvUdiv(lhs, rhs)
114 | TermKind::BvSdiv(lhs, rhs)
115 | TermKind::BvUrem(lhs, rhs)
116 | TermKind::BvSrem(lhs, rhs) => {
117 if self.tracked_compound_terms.contains(&term_id) {
118 return;
119 }
120 self.tracked_compound_terms.insert(term_id);
121 self.has_bv_arith_ops = true;
122 let (l, r) = (*lhs, *rhs);
123 self.track_theory_vars(l, manager);
124 self.track_theory_vars(r, manager);
125 }
126 TermKind::Neg(arg) | TermKind::Not(arg) | TermKind::BvNot(arg) => {
127 if self.tracked_compound_terms.contains(&term_id) {
128 return;
129 }
130 self.tracked_compound_terms.insert(term_id);
131 let a = *arg;
132 self.track_theory_vars(a, manager);
133 }
134 TermKind::Ite(cond, then_br, else_br) => {
135 if self.tracked_compound_terms.contains(&term_id) {
136 return;
137 }
138 self.tracked_compound_terms.insert(term_id);
139 let (c, t, e) = (*cond, *then_br, *else_br);
140 self.track_theory_vars(c, manager);
141 self.track_theory_vars(t, manager);
142 self.track_theory_vars(e, manager);
143 }
144 TermKind::Apply { args, .. } => {
167 let is_int = term.sort == manager.sorts.int_sort;
168 let is_real = term.sort == manager.sorts.real_sort;
169 if is_int || is_real {
170 let has_conflicting_apply_arg = args.iter().any(|&arg| {
179 manager.get(arg).is_some_and(|a| {
180 if let TermKind::Apply {
181 func,
182 args: inner_args,
183 ..
184 } = &a.kind
185 {
186 if inner_args.is_empty() {
187 return false;
188 }
189 let fname = manager.resolve_str(*func);
190 let is_skolem = fname.starts_with("sk!");
191 !is_skolem && self.arith_terms.contains(&arg)
192 } else {
193 false
194 }
195 })
196 });
197 if !has_conflicting_apply_arg && !self.arith_terms.contains(&term_id) {
198 self.arith_terms.insert(term_id);
199 self.trail.push(TrailOp::ArithTermAdded { term: term_id });
200 self.arith.intern(term_id);
201 }
202 }
203 }
204
205 TermKind::Select(_, _) => {
212 let is_int = term.sort == manager.sorts.int_sort;
213 let is_real = term.sort == manager.sorts.real_sort;
214 if (is_int || is_real) && !self.arith_terms.contains(&term_id) {
215 self.arith_terms.insert(term_id);
216 self.trail.push(TrailOp::ArithTermAdded { term: term_id });
217 self.arith.intern(term_id);
218 }
219 }
220
221 _ => {}
223 }
224 }
225
226 pub(super) fn parse_arith_comparison(
233 &mut self,
234 lhs: TermId,
235 rhs: TermId,
236 constraint_type: ArithConstraintType,
237 reason: TermId,
238 manager: &TermManager,
239 ) -> Option<ParsedArithConstraint> {
240 if let Some(cached) = self.arith_parse_cache.get(&reason) {
242 return cached.clone();
243 }
244
245 let mut terms: SmallVec<[(TermId, Rational64); 4]> = SmallVec::new();
246 let mut constant = Rational64::zero();
247
248 let lhs_ok =
250 self.extract_linear_terms(lhs, Rational64::one(), &mut terms, &mut constant, manager);
251 if lhs_ok.is_none() {
252 self.arith_parse_cache.insert(reason, None);
253 return None;
254 }
255
256 let rhs_ok =
259 self.extract_linear_terms(rhs, -Rational64::one(), &mut terms, &mut constant, manager);
260 if rhs_ok.is_none() {
261 self.arith_parse_cache.insert(reason, None);
262 return None;
263 }
264
265 let mut combined: FxHashMap<TermId, Rational64> = FxHashMap::default();
267 for (term, coef) in terms {
268 *combined.entry(term).or_insert(Rational64::zero()) += coef;
269 }
270
271 let final_terms: SmallVec<[(TermId, Rational64); 4]> =
273 combined.into_iter().filter(|(_, c)| !c.is_zero()).collect();
274
275 let result = ParsedArithConstraint {
276 terms: final_terms,
277 constant: -constant, constraint_type,
279 reason_term: reason,
280 };
281
282 self.arith_parse_cache.insert(reason, Some(result.clone()));
283 Some(result)
284 }
285
286 #[allow(clippy::only_used_in_recursion)]
289 pub(super) fn extract_linear_terms(
290 &self,
291 term_id: TermId,
292 scale: Rational64,
293 terms: &mut SmallVec<[(TermId, Rational64); 4]>,
294 constant: &mut Rational64,
295 manager: &TermManager,
296 ) -> Option<()> {
297 let term = manager.get(term_id)?;
298
299 match &term.kind {
300 TermKind::IntConst(n) => {
302 if let Some(val) = n.to_i64() {
303 *constant += scale * Rational64::from_integer(val);
304 Some(())
305 } else {
306 None
308 }
309 }
310
311 TermKind::RealConst(r) => {
313 *constant += scale * *r;
314 Some(())
315 }
316
317 TermKind::BitVecConst { value, .. } => {
319 if let Some(val) = value.to_i64() {
320 *constant += scale * Rational64::from_integer(val);
321 Some(())
322 } else {
323 None
325 }
326 }
327
328 TermKind::Var(_) => {
330 terms.push((term_id, scale));
331 Some(())
332 }
333
334 TermKind::Apply { args, .. } => {
347 let sort = term.sort;
348 let is_numeric = sort == manager.sorts.int_sort || sort == manager.sorts.real_sort;
349 if is_numeric {
350 let has_conflicting_apply_arg = args.iter().any(|&arg| {
356 manager.get(arg).is_some_and(|a| {
357 if let TermKind::Apply {
358 func,
359 args: inner_args,
360 ..
361 } = &a.kind
362 {
363 if inner_args.is_empty() {
364 return false;
365 }
366 let fname = manager.resolve_str(*func);
367 let is_skolem = fname.starts_with("sk!");
368 !is_skolem && self.arith_terms.contains(&arg)
369 } else {
370 false
371 }
372 })
373 });
374 if !has_conflicting_apply_arg {
375 terms.push((term_id, scale));
376 Some(())
377 } else {
378 None
380 }
381 } else {
382 None
384 }
385 }
386
387 TermKind::Select(_, _) => {
392 let sort = term.sort;
393 let is_numeric = sort == manager.sorts.int_sort || sort == manager.sorts.real_sort;
394 if is_numeric {
395 terms.push((term_id, scale));
396 Some(())
397 } else {
398 None
400 }
401 }
402
403 TermKind::Add(args) => {
405 for &arg in args {
406 self.extract_linear_terms(arg, scale, terms, constant, manager)?;
407 }
408 Some(())
409 }
410
411 TermKind::Sub(lhs, rhs) => {
413 self.extract_linear_terms(*lhs, scale, terms, constant, manager)?;
414 self.extract_linear_terms(*rhs, -scale, terms, constant, manager)?;
415 Some(())
416 }
417
418 TermKind::Neg(arg) => self.extract_linear_terms(*arg, -scale, terms, constant, manager),
420
421 TermKind::Mul(args) => {
423 let mut const_product = Rational64::one();
425 let mut var_term: Option<TermId> = None;
426
427 for &arg in args {
428 let arg_term = manager.get(arg)?;
429 match &arg_term.kind {
430 TermKind::IntConst(n) => {
431 if let Some(val) = n.to_i64() {
432 const_product *= Rational64::from_integer(val);
433 } else {
434 return None; }
436 }
437 TermKind::RealConst(r) => {
438 const_product *= *r;
439 }
440 _ => {
441 if var_term.is_some() {
442 return None;
444 }
445 var_term = Some(arg);
446 }
447 }
448 }
449
450 let new_scale = scale * const_product;
451 match var_term {
452 Some(v) => self.extract_linear_terms(v, new_scale, terms, constant, manager),
453 None => {
454 *constant += new_scale;
456 Some(())
457 }
458 }
459 }
460
461 _ => None,
463 }
464 }
465
466 pub fn assert(&mut self, term: TermId, manager: &mut TermManager) {
468 let index = self.assertions.len();
469 self.assertions.push(term);
470 self.trail.push(TrailOp::AssertionAdded { index });
471 self.invalidate_fp_cache();
472
473 if let Some(t) = manager.get(term) {
475 match t.kind {
476 TermKind::False => {
477 if !self.has_false_assertion {
479 self.has_false_assertion = true;
480 self.trail.push(TrailOp::FalseAssertionSet);
481 }
482 if self.produce_unsat_cores {
483 let na_index = self.named_assertions.len();
484 self.named_assertions.push(NamedAssertion {
485 term,
486 name: None,
487 index: index as u32,
488 });
489 self.trail
490 .push(TrailOp::NamedAssertionAdded { index: na_index });
491 }
492 return;
493 }
494 TermKind::True => {
495 if self.produce_unsat_cores {
497 let na_index = self.named_assertions.len();
498 self.named_assertions.push(NamedAssertion {
499 term,
500 name: None,
501 index: index as u32,
502 });
503 self.trail
504 .push(TrailOp::NamedAssertionAdded { index: na_index });
505 }
506 return;
507 }
508 _ => {}
509 }
510 }
511
512 let term_to_encode = if self.config.simplify {
514 self.simplifier.simplify(term, manager)
515 } else {
516 term
517 };
518
519 if let Some(t) = manager.get(term_to_encode) {
521 match t.kind {
522 TermKind::False => {
523 if !self.has_false_assertion {
524 self.has_false_assertion = true;
525 self.trail.push(TrailOp::FalseAssertionSet);
526 }
527 return;
528 }
529 TermKind::True => {
530 return;
532 }
533 _ => {}
534 }
535 }
536
537 if let Some(t) = manager.get(term_to_encode).cloned() {
540 if let TermKind::Eq(lhs, rhs) = &t.kind {
541 if let Some((var_term, constructor)) =
542 self.extract_dt_var_constructor(*lhs, *rhs, manager)
543 {
544 if let Some(&existing_con) = self.dt_var_constructors.get(&var_term) {
545 if existing_con != constructor {
546 if !self.has_false_assertion {
548 self.has_false_assertion = true;
549 self.trail.push(TrailOp::FalseAssertionSet);
550 }
551 return;
552 }
553 } else {
554 self.dt_var_constructors.insert(var_term, constructor);
555 }
556 }
557 }
558 }
559
560 if self.polarity_aware {
562 self.collect_polarities(term_to_encode, Polarity::Positive, manager);
563 }
564
565 let lit = self.encode(term_to_encode, manager);
567 self.sat.add_clause([lit]);
568
569 self.add_arith_diseq_split(term_to_encode, manager);
574
575 if self.produce_unsat_cores {
576 let na_index = self.named_assertions.len();
577 self.named_assertions.push(NamedAssertion {
578 term,
579 name: None,
580 index: index as u32,
581 });
582 self.trail
583 .push(TrailOp::NamedAssertionAdded { index: na_index });
584 }
585 }
586
587 pub fn assert_named(&mut self, term: TermId, name: &str, manager: &mut TermManager) {
589 let index = self.assertions.len();
590 self.assertions.push(term);
591 self.trail.push(TrailOp::AssertionAdded { index });
592 self.invalidate_fp_cache();
593
594 if let Some(t) = manager.get(term) {
596 match t.kind {
597 TermKind::False => {
598 if !self.has_false_assertion {
600 self.has_false_assertion = true;
601 self.trail.push(TrailOp::FalseAssertionSet);
602 }
603 if self.produce_unsat_cores {
604 let na_index = self.named_assertions.len();
605 self.named_assertions.push(NamedAssertion {
606 term,
607 name: Some(name.to_string()),
608 index: index as u32,
609 });
610 self.trail
611 .push(TrailOp::NamedAssertionAdded { index: na_index });
612 }
613 return;
614 }
615 TermKind::True => {
616 if self.produce_unsat_cores {
618 let na_index = self.named_assertions.len();
619 self.named_assertions.push(NamedAssertion {
620 term,
621 name: Some(name.to_string()),
622 index: index as u32,
623 });
624 self.trail
625 .push(TrailOp::NamedAssertionAdded { index: na_index });
626 }
627 return;
628 }
629 _ => {}
630 }
631 }
632
633 if self.polarity_aware {
635 self.collect_polarities(term, Polarity::Positive, manager);
636 }
637
638 let lit = self.encode(term, manager);
640 self.sat.add_clause([lit]);
641
642 self.add_arith_diseq_split(term, manager);
644
645 if self.produce_unsat_cores {
646 let na_index = self.named_assertions.len();
647 self.named_assertions.push(NamedAssertion {
648 term,
649 name: Some(name.to_string()),
650 index: index as u32,
651 });
652 self.trail
653 .push(TrailOp::NamedAssertionAdded { index: na_index });
654 }
655 }
656
657 #[must_use]
659 pub fn get_unsat_core(&self) -> Option<&UnsatCore> {
660 self.unsat_core.as_ref()
661 }
662
663 pub(super) fn encode(&mut self, term: TermId, manager: &mut TermManager) -> Lit {
665 let Some(t) = manager.get(term).cloned() else {
667 let var = self.get_or_create_var(term);
668 return Lit::pos(var);
669 };
670
671 match &t.kind {
672 TermKind::True => {
673 let var = self.get_or_create_var(manager.mk_true());
674 self.sat.add_clause([Lit::pos(var)]);
675 Lit::pos(var)
676 }
677 TermKind::False => {
678 let var = self.get_or_create_var(manager.mk_false());
679 self.sat.add_clause([Lit::neg(var)]);
680 Lit::neg(var)
681 }
682 TermKind::Var(_) => {
683 let var = self.get_or_create_var(term);
684 let is_int = t.sort == manager.sorts.int_sort;
686 let is_real = t.sort == manager.sorts.real_sort;
687
688 if is_int || is_real {
689 if !self.arith_terms.contains(&term) {
691 self.arith_terms.insert(term);
692 self.trail.push(TrailOp::ArithTermAdded { term });
693 self.arith.intern(term);
695 }
696 } else if let Some(sort) = manager.sorts.get(t.sort)
697 && sort.is_bitvec()
698 && !self.bv_terms.contains(&term)
699 {
700 self.bv_terms.insert(term);
701 self.trail.push(TrailOp::BvTermAdded { term });
702 if let Some(width) = sort.bitvec_width() {
704 self.bv.new_bv(term, width);
705 }
706 }
707 Lit::pos(var)
708 }
709 TermKind::Not(arg) => {
710 let arg_lit = self.encode(*arg, manager);
711 arg_lit.negate()
712 }
713 TermKind::And(args) => {
714 let result_var = self.get_or_create_var(term);
715 let result = Lit::pos(result_var);
716
717 let mut arg_lits: Vec<Lit> = Vec::new();
718 for &arg in args {
719 arg_lits.push(self.encode(arg, manager));
720 }
721
722 let polarity = if self.polarity_aware {
724 self.polarities
725 .get(&term)
726 .copied()
727 .unwrap_or(Polarity::Both)
728 } else {
729 Polarity::Both
730 };
731
732 if polarity != Polarity::Negative {
735 for &arg in &arg_lits {
736 self.sat.add_clause([result.negate(), arg]);
737 }
738 }
739
740 if polarity != Polarity::Positive {
743 let mut clause: Vec<Lit> = arg_lits.iter().map(|l| l.negate()).collect();
744 clause.push(result);
745 self.sat.add_clause(clause);
746 }
747
748 result
749 }
750 TermKind::Or(args) => {
751 let result_var = self.get_or_create_var(term);
752 let result = Lit::pos(result_var);
753
754 let mut arg_lits: Vec<Lit> = Vec::new();
755 for &arg in args {
756 arg_lits.push(self.encode(arg, manager));
757 }
758
759 let polarity = if self.polarity_aware {
761 self.polarities
762 .get(&term)
763 .copied()
764 .unwrap_or(Polarity::Both)
765 } else {
766 Polarity::Both
767 };
768
769 if polarity != Polarity::Negative {
772 let mut clause: Vec<Lit> = vec![result.negate()];
773 clause.extend(arg_lits.iter().copied());
774 self.sat.add_clause(clause);
775 }
776
777 if polarity != Polarity::Positive {
780 for &arg in &arg_lits {
781 self.sat.add_clause([arg.negate(), result]);
782 }
783 }
784
785 result
786 }
787 TermKind::Xor(lhs, rhs) => {
788 let lhs_lit = self.encode(*lhs, manager);
789 let rhs_lit = self.encode(*rhs, manager);
790
791 let result_var = self.get_or_create_var(term);
792 let result = Lit::pos(result_var);
793
794 self.sat.add_clause([result.negate(), lhs_lit, rhs_lit]);
799 self.sat
801 .add_clause([result.negate(), lhs_lit.negate(), rhs_lit.negate()]);
802
803 self.sat.add_clause([lhs_lit.negate(), rhs_lit, result]);
805 self.sat.add_clause([lhs_lit, rhs_lit.negate(), result]);
807
808 result
809 }
810 TermKind::Implies(lhs, rhs) => {
811 let lhs_lit = self.encode(*lhs, manager);
812 let rhs_lit = self.encode(*rhs, manager);
813
814 let result_var = self.get_or_create_var(term);
815 let result = Lit::pos(result_var);
816
817 self.sat
820 .add_clause([result.negate(), lhs_lit.negate(), rhs_lit]);
821
822 self.sat.add_clause([lhs_lit, result]);
825 self.sat.add_clause([rhs_lit.negate(), result]);
826
827 result
828 }
829 TermKind::Ite(cond, then_br, else_br) => {
830 let cond_lit = self.encode(*cond, manager);
831 let then_lit = self.encode(*then_br, manager);
832 let else_lit = self.encode(*else_br, manager);
833
834 let result_var = self.get_or_create_var(term);
835 let result = Lit::pos(result_var);
836
837 self.sat
840 .add_clause([cond_lit.negate(), result.negate(), then_lit]);
841 self.sat
843 .add_clause([cond_lit.negate(), then_lit.negate(), result]);
844
845 self.sat.add_clause([cond_lit, result.negate(), else_lit]);
847 self.sat.add_clause([cond_lit, else_lit.negate(), result]);
849
850 result
851 }
852 TermKind::Eq(lhs, rhs) => {
853 let lhs_term = manager.get(*lhs);
855 let is_bool_eq = lhs_term.is_some_and(|t| t.sort == manager.sorts.bool_sort);
856
857 if is_bool_eq {
858 let lhs_lit = self.encode(*lhs, manager);
860 let rhs_lit = self.encode(*rhs, manager);
861
862 let result_var = self.get_or_create_var(term);
863 let result = Lit::pos(result_var);
864
865 self.sat
868 .add_clause([result.negate(), lhs_lit.negate(), rhs_lit]);
869 self.sat
870 .add_clause([result.negate(), rhs_lit.negate(), lhs_lit]);
871
872 self.sat.add_clause([lhs_lit, rhs_lit, result]);
874 self.sat
875 .add_clause([lhs_lit.negate(), rhs_lit.negate(), result]);
876
877 result
878 } else {
879 let var = self.get_or_create_var(term);
882 self.var_to_constraint
883 .insert(var, Constraint::Eq(*lhs, *rhs));
884 self.trail.push(TrailOp::ConstraintAdded { var });
885
886 self.track_theory_vars(*lhs, manager);
888 self.track_theory_vars(*rhs, manager);
889
890 let is_arith = lhs_term.is_some_and(|t| {
893 t.sort == manager.sorts.int_sort || t.sort == manager.sorts.real_sort
894 });
895 if is_arith {
896 if let Some(parsed) = self.parse_arith_comparison(
899 *lhs,
900 *rhs,
901 ArithConstraintType::Le,
902 term,
903 manager,
904 ) {
905 self.var_to_parsed_arith.insert(var, parsed);
906 }
907 }
908
909 Lit::pos(var)
910 }
911 }
912 TermKind::Distinct(args) => {
913 if args.len() <= 1 {
916 let var = self.get_or_create_var(manager.mk_true());
918 return Lit::pos(var);
919 }
920
921 let result_var = self.get_or_create_var(term);
922 let result = Lit::pos(result_var);
923
924 let mut diseq_lits = Vec::new();
925 for i in 0..args.len() {
926 for j in (i + 1)..args.len() {
927 let eq = manager.mk_eq(args[i], args[j]);
928 let eq_lit = self.encode(eq, manager);
929 diseq_lits.push(eq_lit.negate());
930 }
931 }
932
933 for &diseq in &diseq_lits {
935 self.sat.add_clause([result.negate(), diseq]);
936 }
937
938 let mut clause: Vec<Lit> = diseq_lits.iter().map(|l| l.negate()).collect();
940 clause.push(result);
941 self.sat.add_clause(clause);
942
943 result
944 }
945 TermKind::Let { bindings, body } => {
946 let substituted = *body;
950 for (name, value) in bindings.iter().rev() {
951 let _ = (name, value);
954 }
955 self.encode(substituted, manager)
956 }
957 TermKind::IntConst(_) | TermKind::RealConst(_) | TermKind::BitVecConst { .. } => {
960 let var = self.get_or_create_var(term);
963 Lit::pos(var)
964 }
965 TermKind::Neg(_)
966 | TermKind::Add(_)
967 | TermKind::Sub(_, _)
968 | TermKind::Mul(_)
969 | TermKind::Div(_, _)
970 | TermKind::Mod(_, _) => {
971 let var = self.get_or_create_var(term);
973 Lit::pos(var)
974 }
975 TermKind::Lt(lhs, rhs) => {
976 let var = self.get_or_create_var(term);
978 self.var_to_constraint
979 .insert(var, Constraint::Lt(*lhs, *rhs));
980 self.trail.push(TrailOp::ConstraintAdded { var });
981 if let Some(parsed) =
983 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Lt, term, manager)
984 {
985 self.var_to_parsed_arith.insert(var, parsed);
986 }
987 self.track_theory_vars(*lhs, manager);
989 self.track_theory_vars(*rhs, manager);
990 Lit::pos(var)
991 }
992 TermKind::Le(lhs, rhs) => {
993 let var = self.get_or_create_var(term);
995 self.var_to_constraint
996 .insert(var, Constraint::Le(*lhs, *rhs));
997 self.trail.push(TrailOp::ConstraintAdded { var });
998 if let Some(parsed) =
1000 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Le, term, manager)
1001 {
1002 self.var_to_parsed_arith.insert(var, parsed);
1003 }
1004 self.track_theory_vars(*lhs, manager);
1006 self.track_theory_vars(*rhs, manager);
1007 Lit::pos(var)
1008 }
1009 TermKind::Gt(lhs, rhs) => {
1010 let var = self.get_or_create_var(term);
1012 self.var_to_constraint
1013 .insert(var, Constraint::Gt(*lhs, *rhs));
1014 self.trail.push(TrailOp::ConstraintAdded { var });
1015 if let Some(parsed) =
1017 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Gt, term, manager)
1018 {
1019 self.var_to_parsed_arith.insert(var, parsed);
1020 }
1021 self.track_theory_vars(*lhs, manager);
1023 self.track_theory_vars(*rhs, manager);
1024 Lit::pos(var)
1025 }
1026 TermKind::Ge(lhs, rhs) => {
1027 let var = self.get_or_create_var(term);
1029 self.var_to_constraint
1030 .insert(var, Constraint::Ge(*lhs, *rhs));
1031 self.trail.push(TrailOp::ConstraintAdded { var });
1032 if let Some(parsed) =
1034 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Ge, term, manager)
1035 {
1036 self.var_to_parsed_arith.insert(var, parsed);
1037 }
1038 self.track_theory_vars(*lhs, manager);
1040 self.track_theory_vars(*rhs, manager);
1041 Lit::pos(var)
1042 }
1043 TermKind::BvConcat(_, _)
1044 | TermKind::BvExtract { .. }
1045 | TermKind::BvNot(_)
1046 | TermKind::BvAnd(_, _)
1047 | TermKind::BvOr(_, _)
1048 | TermKind::BvXor(_, _)
1049 | TermKind::BvAdd(_, _)
1050 | TermKind::BvSub(_, _)
1051 | TermKind::BvMul(_, _)
1052 | TermKind::BvShl(_, _)
1053 | TermKind::BvLshr(_, _)
1054 | TermKind::BvAshr(_, _) => {
1055 let var = self.get_or_create_var(term);
1057 Lit::pos(var)
1058 }
1059 TermKind::BvUdiv(_, _)
1060 | TermKind::BvSdiv(_, _)
1061 | TermKind::BvUrem(_, _)
1062 | TermKind::BvSrem(_, _) => {
1063 self.has_bv_arith_ops = true;
1066 let var = self.get_or_create_var(term);
1067 Lit::pos(var)
1068 }
1069 TermKind::BvUlt(lhs, rhs) => {
1070 let var = self.get_or_create_var(term);
1072 self.var_to_constraint
1073 .insert(var, Constraint::Lt(*lhs, *rhs));
1074 self.trail.push(TrailOp::ConstraintAdded { var });
1075 if let Some(parsed) =
1077 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Lt, term, manager)
1078 {
1079 self.var_to_parsed_arith.insert(var, parsed);
1080 }
1081 self.track_theory_vars(*lhs, manager);
1083 self.track_theory_vars(*rhs, manager);
1084 Lit::pos(var)
1085 }
1086 TermKind::BvUle(lhs, rhs) => {
1087 let var = self.get_or_create_var(term);
1089 self.var_to_constraint
1090 .insert(var, Constraint::Le(*lhs, *rhs));
1091 self.trail.push(TrailOp::ConstraintAdded { var });
1092 if let Some(parsed) =
1093 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Le, term, manager)
1094 {
1095 self.var_to_parsed_arith.insert(var, parsed);
1096 }
1097 self.track_theory_vars(*lhs, manager);
1099 self.track_theory_vars(*rhs, manager);
1100 Lit::pos(var)
1101 }
1102 TermKind::BvSlt(lhs, rhs) => {
1103 let var = self.get_or_create_var(term);
1105 self.var_to_constraint
1106 .insert(var, Constraint::Lt(*lhs, *rhs));
1107 self.trail.push(TrailOp::ConstraintAdded { var });
1108 if let Some(parsed) =
1109 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Lt, term, manager)
1110 {
1111 self.var_to_parsed_arith.insert(var, parsed);
1112 }
1113 self.track_theory_vars(*lhs, manager);
1115 self.track_theory_vars(*rhs, manager);
1116 Lit::pos(var)
1117 }
1118 TermKind::BvSle(lhs, rhs) => {
1119 let var = self.get_or_create_var(term);
1121 self.var_to_constraint
1122 .insert(var, Constraint::Le(*lhs, *rhs));
1123 self.trail.push(TrailOp::ConstraintAdded { var });
1124 if let Some(parsed) =
1125 self.parse_arith_comparison(*lhs, *rhs, ArithConstraintType::Le, term, manager)
1126 {
1127 self.var_to_parsed_arith.insert(var, parsed);
1128 }
1129 self.track_theory_vars(*lhs, manager);
1131 self.track_theory_vars(*rhs, manager);
1132 Lit::pos(var)
1133 }
1134 TermKind::Select(_, _) | TermKind::Store(_, _, _) => {
1135 let var = self.get_or_create_var(term);
1137 Lit::pos(var)
1138 }
1139 TermKind::Apply { .. } => {
1140 let var = self.get_or_create_var(term);
1142 if t.sort == manager.sorts.bool_sort {
1148 self.var_to_constraint
1149 .insert(var, Constraint::BoolApp(term));
1150 self.trail.push(TrailOp::ConstraintAdded { var });
1151 }
1152 Lit::pos(var)
1153 }
1154 TermKind::Forall {
1155 patterns,
1156 body,
1157 vars,
1158 } => {
1159 self.has_quantifiers = true;
1161
1162 let body_id = *body;
1165 let vars_clone = vars.clone();
1166 let patterns_clone = patterns.clone();
1167 let body_is_exists = manager
1168 .get(body_id)
1169 .map(|t| matches!(t.kind, TermKind::Exists { .. }))
1170 .unwrap_or(false);
1171
1172 if body_is_exists {
1173 #[cfg(feature = "std")]
1177 {
1178 let mut sk_ctx = crate::skolemization::SkolemizationContext::new();
1179 if let Ok(skolemized) = sk_ctx.skolemize(manager, term) {
1180 self.mbqi.add_quantifier(skolemized, manager);
1182 let _ = self.ematch_engine.register_quantifier(skolemized, manager);
1184
1185 self.collect_skolem_candidates(skolemized, manager);
1190 } else {
1191 self.mbqi.add_quantifier(term, manager);
1193 let _ = self.ematch_engine.register_quantifier(term, manager);
1194 }
1195 }
1196 #[cfg(not(feature = "std"))]
1197 {
1198 self.mbqi.add_quantifier(term, manager);
1199 let _ = self.ematch_engine.register_quantifier(term, manager);
1200 }
1201 } else {
1202 self.mbqi.add_quantifier(term, manager);
1203 let _ = self.ematch_engine.register_quantifier(term, manager);
1205 }
1206
1207 for pattern in &patterns_clone {
1209 for &trigger in pattern {
1210 self.mbqi.collect_ground_terms(trigger, manager);
1211 }
1212 }
1213 let var = self.get_or_create_var(term);
1215 Lit::pos(var)
1216 }
1217 TermKind::Exists { patterns, .. } => {
1218 self.has_quantifiers = true;
1220 self.mbqi.add_quantifier(term, manager);
1221 for pattern in patterns {
1223 for &trigger in pattern {
1224 self.mbqi.collect_ground_terms(trigger, manager);
1225 }
1226 }
1227 let var = self.get_or_create_var(term);
1229 Lit::pos(var)
1230 }
1231 TermKind::StringLit(_)
1233 | TermKind::StrConcat(_, _)
1234 | TermKind::StrLen(_)
1235 | TermKind::StrSubstr(_, _, _)
1236 | TermKind::StrAt(_, _)
1237 | TermKind::StrReplace(_, _, _)
1238 | TermKind::StrReplaceAll(_, _, _)
1239 | TermKind::StrToInt(_)
1240 | TermKind::IntToStr(_)
1241 | TermKind::StrInRe(_, _) => {
1242 let var = self.get_or_create_var(term);
1244 Lit::pos(var)
1245 }
1246 TermKind::StrContains(_, _)
1247 | TermKind::StrPrefixOf(_, _)
1248 | TermKind::StrSuffixOf(_, _)
1249 | TermKind::StrIndexOf(_, _, _) => {
1250 let var = self.get_or_create_var(term);
1252 Lit::pos(var)
1253 }
1254 TermKind::FpLit { .. }
1256 | TermKind::FpPlusInfinity { .. }
1257 | TermKind::FpMinusInfinity { .. }
1258 | TermKind::FpPlusZero { .. }
1259 | TermKind::FpMinusZero { .. }
1260 | TermKind::FpNaN { .. } => {
1261 let var = self.get_or_create_var(term);
1263 Lit::pos(var)
1264 }
1265 TermKind::FpAbs(_)
1267 | TermKind::FpNeg(_)
1268 | TermKind::FpSqrt(_, _)
1269 | TermKind::FpRoundToIntegral(_, _)
1270 | TermKind::FpAdd(_, _, _)
1271 | TermKind::FpSub(_, _, _)
1272 | TermKind::FpMul(_, _, _)
1273 | TermKind::FpDiv(_, _, _)
1274 | TermKind::FpRem(_, _)
1275 | TermKind::FpMin(_, _)
1276 | TermKind::FpMax(_, _)
1277 | TermKind::FpFma(_, _, _, _) => {
1278 let var = self.get_or_create_var(term);
1280 Lit::pos(var)
1281 }
1282 TermKind::FpLeq(_, _)
1284 | TermKind::FpLt(_, _)
1285 | TermKind::FpGeq(_, _)
1286 | TermKind::FpGt(_, _)
1287 | TermKind::FpEq(_, _)
1288 | TermKind::FpIsNormal(_)
1289 | TermKind::FpIsSubnormal(_)
1290 | TermKind::FpIsZero(_)
1291 | TermKind::FpIsInfinite(_)
1292 | TermKind::FpIsNaN(_)
1293 | TermKind::FpIsNegative(_)
1294 | TermKind::FpIsPositive(_) => {
1295 let var = self.get_or_create_var(term);
1297 Lit::pos(var)
1298 }
1299 TermKind::FpToFp { .. }
1301 | TermKind::FpToSBV { .. }
1302 | TermKind::FpToUBV { .. }
1303 | TermKind::FpToReal(_)
1304 | TermKind::RealToFp { .. }
1305 | TermKind::SBVToFp { .. }
1306 | TermKind::UBVToFp { .. } => {
1307 let var = self.get_or_create_var(term);
1309 Lit::pos(var)
1310 }
1311 TermKind::DtConstructor { .. }
1313 | TermKind::DtTester { .. }
1314 | TermKind::DtSelector { .. } => {
1315 let var = self.get_or_create_var(term);
1317 Lit::pos(var)
1318 }
1319 TermKind::Match { .. } => {
1321 let var = self.get_or_create_var(term);
1323 Lit::pos(var)
1324 }
1325 }
1326 }
1327
1328 fn collect_skolem_candidates(&mut self, term: TermId, manager: &TermManager) {
1335 let mut visited = FxHashSet::default();
1336 self.collect_skolem_candidates_rec(term, manager, &mut visited);
1337 }
1338
1339 fn collect_skolem_candidates_rec(
1340 &mut self,
1341 term: TermId,
1342 manager: &TermManager,
1343 visited: &mut FxHashSet<TermId>,
1344 ) {
1345 if !visited.insert(term) {
1346 return;
1347 }
1348 let Some(t) = manager.get(term) else {
1349 return;
1350 };
1351 match &t.kind {
1352 TermKind::Apply { func, args } => {
1353 let fname = manager.resolve_str(*func);
1354 if fname.starts_with("sk") || fname.starts_with("skf") {
1355 self.mbqi.add_candidate(term, t.sort);
1357 }
1358 for &arg in args.iter() {
1359 self.collect_skolem_candidates_rec(arg, manager, visited);
1360 }
1361 }
1362 TermKind::Forall { body, .. } | TermKind::Exists { body, .. } => {
1363 self.collect_skolem_candidates_rec(*body, manager, visited);
1364 }
1365 TermKind::And(args) | TermKind::Or(args) => {
1366 for &a in args {
1367 self.collect_skolem_candidates_rec(a, manager, visited);
1368 }
1369 }
1370 TermKind::Not(a) | TermKind::Neg(a) => {
1371 self.collect_skolem_candidates_rec(*a, manager, visited);
1372 }
1373 TermKind::Implies(a, b)
1374 | TermKind::Eq(a, b)
1375 | TermKind::Lt(a, b)
1376 | TermKind::Le(a, b)
1377 | TermKind::Gt(a, b)
1378 | TermKind::Ge(a, b)
1379 | TermKind::Sub(a, b)
1380 | TermKind::Div(a, b)
1381 | TermKind::Mod(a, b) => {
1382 self.collect_skolem_candidates_rec(*a, manager, visited);
1383 self.collect_skolem_candidates_rec(*b, manager, visited);
1384 }
1385 TermKind::Add(args) | TermKind::Mul(args) => {
1386 for &a in args.iter() {
1387 self.collect_skolem_candidates_rec(a, manager, visited);
1388 }
1389 }
1390 TermKind::Ite(c, t_br, e) => {
1391 self.collect_skolem_candidates_rec(*c, manager, visited);
1392 self.collect_skolem_candidates_rec(*t_br, manager, visited);
1393 self.collect_skolem_candidates_rec(*e, manager, visited);
1394 }
1395 TermKind::Select(a, i) => {
1396 self.collect_skolem_candidates_rec(*a, manager, visited);
1397 self.collect_skolem_candidates_rec(*i, manager, visited);
1398 }
1399 TermKind::Store(a, i, v) => {
1400 self.collect_skolem_candidates_rec(*a, manager, visited);
1401 self.collect_skolem_candidates_rec(*i, manager, visited);
1402 self.collect_skolem_candidates_rec(*v, manager, visited);
1403 }
1404 _ => {}
1405 }
1406 }
1407
1408 pub(super) fn add_arith_diseq_splits_for_sat_model(&mut self, manager: &mut TermManager) {
1414 use super::types::Constraint;
1415 use oxiz_sat::LBool;
1416
1417 let pairs: Vec<(TermId, TermId)> = self
1418 .var_to_constraint
1419 .iter()
1420 .filter_map(|(&var, constraint)| {
1421 if let Constraint::Eq(lhs, rhs) = constraint {
1422 let lhs_is_numeric = manager.get(*lhs).is_some_and(|lt| {
1424 lt.sort == manager.sorts.int_sort || lt.sort == manager.sorts.real_sort
1425 });
1426 if lhs_is_numeric && self.sat.model_value(var) == LBool::False {
1427 Some((*lhs, *rhs))
1428 } else {
1429 None
1430 }
1431 } else {
1432 None
1433 }
1434 })
1435 .collect();
1436
1437 for (lhs, rhs) in pairs {
1438 let lt_term = manager.mk_lt(lhs, rhs);
1439 let gt_term = manager.mk_gt(lhs, rhs);
1440 let lt_lit = self.encode(lt_term, manager);
1442 let gt_lit = self.encode(gt_term, manager);
1443 self.sat.add_clause([lt_lit, gt_lit]);
1444 }
1445 }
1446
1447 pub(super) fn add_arith_diseq_split(&mut self, term: TermId, manager: &mut TermManager) {
1452 let mut visited = FxHashSet::default();
1453 self.add_arith_diseq_split_recursive(term, manager, &mut visited);
1454 }
1455
1456 pub(super) fn add_arith_eq_trichotomy(&mut self, term: TermId, manager: &mut TermManager) {
1466 let mut visited = FxHashSet::default();
1467 self.add_arith_eq_trichotomy_recursive(term, manager, &mut visited);
1468 }
1469
1470 fn add_arith_eq_trichotomy_recursive(
1471 &mut self,
1472 term: TermId,
1473 manager: &mut TermManager,
1474 visited: &mut FxHashSet<TermId>,
1475 ) {
1476 if !visited.insert(term) {
1477 return;
1478 }
1479
1480 let Some(t) = manager.get(term).cloned() else {
1481 return;
1482 };
1483
1484 match &t.kind {
1485 TermKind::Eq(lhs, rhs) => {
1486 let lhs_is_numeric = manager.get(*lhs).is_some_and(|lt| {
1487 lt.sort == manager.sorts.int_sort || lt.sort == manager.sorts.real_sort
1488 });
1489 let lhs_is_apply = manager
1496 .get(*lhs)
1497 .is_some_and(|lt| matches!(lt.kind, TermKind::Apply { .. }));
1498 let rhs_is_apply = manager
1499 .get(*rhs)
1500 .is_some_and(|rt| matches!(rt.kind, TermKind::Apply { .. }));
1501 if lhs_is_numeric && (lhs_is_apply || rhs_is_apply) {
1502 let (l, r) = (*lhs, *rhs);
1503 let eq_var = self.get_or_create_var(term);
1505 let eq_lit = Lit::pos(eq_var);
1506 let lt_term = manager.mk_lt(l, r);
1507 let gt_term = manager.mk_gt(l, r);
1508 let lt_lit = self.encode(lt_term, manager);
1509 let gt_lit = self.encode(gt_term, manager);
1510 self.sat.add_clause([eq_lit, lt_lit, gt_lit]);
1511 }
1512 }
1513 TermKind::Not(arg) => {
1514 self.add_arith_eq_trichotomy_recursive(*arg, manager, visited);
1515 }
1516 TermKind::And(args) => {
1517 let args_clone: Vec<TermId> = args.iter().copied().collect();
1518 for arg in args_clone {
1519 self.add_arith_eq_trichotomy_recursive(arg, manager, visited);
1520 }
1521 }
1522 TermKind::Or(args) => {
1523 let args_clone: Vec<TermId> = args.iter().copied().collect();
1524 for arg in args_clone {
1525 self.add_arith_eq_trichotomy_recursive(arg, manager, visited);
1526 }
1527 }
1528 TermKind::Implies(lhs, rhs) => {
1529 let (l, r) = (*lhs, *rhs);
1530 self.add_arith_eq_trichotomy_recursive(l, manager, visited);
1531 self.add_arith_eq_trichotomy_recursive(r, manager, visited);
1532 }
1533 TermKind::Ite(_, then_br, else_br) => {
1534 let (t, e) = (*then_br, *else_br);
1535 self.add_arith_eq_trichotomy_recursive(t, manager, visited);
1536 self.add_arith_eq_trichotomy_recursive(e, manager, visited);
1537 }
1538 _ => {}
1539 }
1540 }
1541
1542 fn add_arith_diseq_split_recursive(
1549 &mut self,
1550 term: TermId,
1551 manager: &mut TermManager,
1552 visited: &mut FxHashSet<TermId>,
1553 ) {
1554 if !visited.insert(term) {
1555 return;
1556 }
1557
1558 let Some(t) = manager.get(term).cloned() else {
1559 return;
1560 };
1561
1562 match &t.kind {
1563 TermKind::Not(inner) => {
1564 let inner_id = *inner;
1565 if let Some(inner_t) = manager.get(inner_id).cloned() {
1566 if let TermKind::Eq(lhs, rhs) = &inner_t.kind {
1567 let lhs_is_numeric = manager.get(*lhs).is_some_and(|lt| {
1568 lt.sort == manager.sorts.int_sort || lt.sort == manager.sorts.real_sort
1569 });
1570 if lhs_is_numeric {
1571 let (l, r) = (*lhs, *rhs);
1572 let lt_term = manager.mk_lt(l, r);
1574 let gt_term = manager.mk_gt(l, r);
1575
1576 let lt_lit = self.encode(lt_term, manager);
1578 let gt_lit = self.encode(gt_term, manager);
1579 self.sat.add_clause([lt_lit, gt_lit]);
1580 }
1581 }
1582 }
1583 self.add_arith_diseq_split_recursive(inner_id, manager, visited);
1585 }
1586 TermKind::And(args) => {
1587 let args_clone: Vec<TermId> = args.iter().copied().collect();
1588 for arg in args_clone {
1589 self.add_arith_diseq_split_recursive(arg, manager, visited);
1590 }
1591 }
1592 TermKind::Or(args) => {
1593 let args_clone: Vec<TermId> = args.iter().copied().collect();
1594 for arg in args_clone {
1595 self.add_arith_diseq_split_recursive(arg, manager, visited);
1596 }
1597 }
1598 TermKind::Implies(_, rhs) => {
1599 let rhs_id = *rhs;
1602 self.add_arith_diseq_split_recursive(rhs_id, manager, visited);
1603 }
1604 TermKind::Ite(_, then_br, else_br) => {
1605 let (t, e) = (*then_br, *else_br);
1606 self.add_arith_diseq_split_recursive(t, manager, visited);
1607 self.add_arith_diseq_split_recursive(e, manager, visited);
1608 }
1609 _ => {}
1610 }
1611 }
1612}