1#![allow(missing_docs)]
7
8#[allow(unused_imports)]
9use crate::prelude::*;
10use core::fmt;
11use oxiz_core::ast::{TermId, TermKind, TermManager};
12use oxiz_core::interner::Spur;
13use oxiz_core::sort::SortId;
14use smallvec::SmallVec;
15#[cfg(feature = "std")]
16use std::time::{Duration, Instant};
17
18use super::conflict_driven::ConflictScores;
19use super::counterexample::CounterExampleGenerator;
20use super::finite_model::FiniteModelFinder;
21use super::heuristics::MBQIBudget;
22use super::instantiation::InstantiationEngine;
23use super::lazy_instantiation::LazyInstantiator;
24use super::model_completion::ModelCompleter;
25use super::{Instantiation, MBQIResult, MBQIStats, QuantifiedFormula, QuantifierId};
26
27pub trait SolverCallback: fmt::Debug {
29 fn on_instantiation(&mut self, inst: &Instantiation);
31
32 fn on_conflict(&mut self, quantifier: TermId, reason: &[TermId]);
34
35 fn on_round_start(&mut self, round: usize);
37
38 fn on_round_end(&mut self, round: usize, result: &MBQIResult);
40
41 fn should_stop(&self) -> bool;
43}
44
45#[derive(Debug)]
47pub struct MBQIIntegration {
48 model_completer: ModelCompleter,
50 instantiation_engine: InstantiationEngine,
52 lazy_instantiator: LazyInstantiator,
54 finite_model_finder: FiniteModelFinder,
56 cex_generator: CounterExampleGenerator,
58 quantifiers: Vec<QuantifiedFormula>,
60 generated_instantiations: FxHashMap<InstantiationKey, usize>,
62 extra_candidates: FxHashMap<SortId, Vec<TermId>>,
64 blind_attempted: bool,
66 current_round: usize,
68 budget: MBQIBudget,
70 conflict_scores: ConflictScores,
72 max_rounds: usize,
74 #[cfg(feature = "std")]
76 time_limit: Option<Duration>,
77 #[cfg(feature = "std")]
79 start_time: Option<Instant>,
80 stats: MBQIStats,
82}
83
84impl MBQIIntegration {
85 pub fn new() -> Self {
87 Self {
88 model_completer: ModelCompleter::new(),
89 instantiation_engine: InstantiationEngine::new(),
90 lazy_instantiator: LazyInstantiator::new(),
91 finite_model_finder: FiniteModelFinder::new(),
92 cex_generator: CounterExampleGenerator::new(),
93 quantifiers: Vec::new(),
94 generated_instantiations: FxHashMap::default(),
95 extra_candidates: FxHashMap::default(),
96 blind_attempted: false,
97 current_round: 0,
98 budget: MBQIBudget::new(1024),
99 conflict_scores: ConflictScores::new(0.95),
100 max_rounds: 100,
101 #[cfg(feature = "std")]
102 time_limit: Some(Duration::from_secs(60)),
103 #[cfg(feature = "std")]
104 start_time: None,
105 stats: MBQIStats::new(),
106 }
107 }
108
109 pub fn add_quantifier(&mut self, term: TermId, manager: &TermManager) {
111 let Some(t) = manager.get(term) else {
112 return;
113 };
114
115 match &t.kind {
116 oxiz_core::ast::TermKind::Forall { vars, body, .. } => {
117 let bound_vars: SmallVec<[(Spur, SortId); 4]> = vars.iter().copied().collect();
118 self.quantifiers
119 .push(QuantifiedFormula::new(term, bound_vars, *body, true));
120 self.stats.num_quantifiers += 1;
121 }
122 oxiz_core::ast::TermKind::Exists { vars, body, .. } => {
123 let bound_vars: SmallVec<[(Spur, SortId); 4]> = vars.iter().copied().collect();
124 self.quantifiers
125 .push(QuantifiedFormula::new(term, bound_vars, *body, false));
126 self.stats.num_quantifiers += 1;
127 }
128 _ => {}
129 }
130 }
131
132 pub fn run(
140 &mut self,
141 partial_model: &FxHashMap<TermId, TermId>,
142 manager: &mut TermManager,
143 callback: &mut dyn SolverCallback,
144 ) -> MBQIResult {
145 #[cfg(feature = "std")]
146 {
147 self.start_time = Some(Instant::now());
148 }
149 self.current_round = 0;
150
151 if self.quantifiers.is_empty() {
152 return MBQIResult::NoQuantifiers;
153 }
154
155 self.cex_generator.clear_cache();
159
160 if self.current_round >= self.max_rounds {
162 self.update_final_stats();
163 return MBQIResult::Unknown;
164 }
165
166 if self.check_timeout() || callback.should_stop() {
167 return MBQIResult::Unknown;
168 }
169
170 self.current_round += 1;
171 if self.current_round > 1 {
172 self.conflict_scores.decay_on_restart();
173 }
174 let quantifier_ids: Vec<QuantifierId> = self.quantifiers.iter().map(|q| q.term).collect();
175 self.budget
176 .carve_per_quantifier(&quantifier_ids, Some(&self.conflict_scores));
177 callback.on_round_start(self.current_round);
178 self.stats.num_checks += 1;
179
180 #[cfg(feature = "std")]
181 let round_start = Instant::now();
182
183 let completed_model =
185 match self
186 .model_completer
187 .complete(partial_model, &self.quantifiers, manager)
188 {
189 Ok(model) => {
190 self.stats.num_completions += 1;
191 model
192 }
193 Err(_) => {
194 callback.on_round_end(self.current_round, &MBQIResult::Unknown);
195 return MBQIResult::Unknown;
196 }
197 };
198
199 #[cfg(feature = "std")]
200 {
201 self.stats.completion_time_us += round_start.elapsed().as_micros() as u64;
202 }
203
204 #[cfg(feature = "std")]
207 let cex_start = Instant::now();
208 let mut all_instantiations = Vec::new();
209 let mut all_evaluations_fully_ground = true;
213
214 let quantifiers: Vec<_> = self.quantifiers.to_vec();
216 for quantifier in &quantifiers {
217 if !quantifier.can_instantiate() {
218 continue;
219 }
220
221 self.cex_generator
224 .inject_extra_candidates(&self.extra_candidates);
225
226 let cex_result = self
229 .cex_generator
230 .generate(quantifier, &completed_model, manager);
231
232 if !cex_result.all_evaluations_ground {
233 all_evaluations_fully_ground = false;
234 }
235
236 self.stats.num_counterexamples += cex_result.counterexamples.len();
237
238 for cex in &cex_result.counterexamples {
239 if !self.budget.consume(quantifier.term, 1) {
240 break;
241 }
242 let ground_body =
244 self.apply_substitution(quantifier.body, &cex.assignment, manager);
245
246 let inst = cex.to_instantiation(ground_body);
247
248 if !self.is_duplicate(&inst) {
249 self.record_instantiation(&inst);
250 callback.on_instantiation(&inst);
251 all_instantiations.push(inst);
252 }
253 }
254
255 if cex_result.counterexamples.is_empty() && quantifier.is_universal {
264 let engine_insts =
265 self.instantiation_engine
266 .instantiate(quantifier, &completed_model, manager);
267
268 for inst in engine_insts {
269 if !self.budget.consume(quantifier.term, 1) {
270 break;
271 }
272 if !self.is_duplicate(&inst) {
273 self.record_instantiation(&inst);
274 callback.on_instantiation(&inst);
275 all_instantiations.push(inst);
276 }
277 }
278 } else if cex_result.counterexamples.is_empty() && !quantifier.is_universal {
279 all_evaluations_fully_ground = false;
282 }
283 }
284
285 #[cfg(feature = "std")]
286 {
287 self.stats.cex_search_time_us += cex_start.elapsed().as_micros() as u64;
288 }
289
290 if all_instantiations.is_empty() {
292 if all_evaluations_fully_ground {
293 let result = MBQIResult::Satisfied;
297 callback.on_round_end(self.current_round, &result);
298 self.update_final_stats();
299 return result;
300 }
301 for quantifier in &quantifiers {
307 if !quantifier.is_universal || !quantifier.can_instantiate() {
308 continue;
309 }
310 let engine_insts =
311 self.instantiation_engine
312 .instantiate(quantifier, &completed_model, manager);
313 for mut inst in engine_insts {
314 if !self.budget.consume(quantifier.term, 1) {
315 break;
316 }
317 inst.result = self.deep_simplify(inst.result, manager);
322 if manager
324 .get(inst.result)
325 .is_some_and(|t| matches!(t.kind, TermKind::True))
326 {
327 continue;
328 }
329 if !self.is_duplicate(&inst) {
330 self.record_instantiation(&inst);
331 callback.on_instantiation(&inst);
332 all_instantiations.push(inst);
333 }
334 }
335 }
336
337 if !all_instantiations.is_empty() {
338 let result = MBQIResult::NewInstantiations(all_instantiations);
339 callback.on_round_end(self.current_round, &result);
340 self.update_final_stats();
341 return result;
342 }
343
344 let result = MBQIResult::Unknown;
346 callback.on_round_end(self.current_round, &result);
347 self.update_final_stats();
348 return result;
349 }
350
351 if self.stats.max_instantiations > 0
353 && self.stats.total_instantiations >= self.stats.max_instantiations
354 {
355 let result = MBQIResult::InstantiationLimit;
356 callback.on_round_end(self.current_round, &result);
357 self.update_final_stats();
358 return result;
359 }
360
361 let result = MBQIResult::NewInstantiations(all_instantiations);
365 callback.on_round_end(self.current_round, &result);
366 self.update_final_stats();
367 result
368 }
369
370 fn apply_substitution(
372 &self,
373 term: TermId,
374 subst: &FxHashMap<Spur, TermId>,
375 manager: &mut TermManager,
376 ) -> TermId {
377 let mut cache = FxHashMap::default();
378 self.apply_substitution_cached(term, subst, manager, &mut cache)
379 }
380
381 fn apply_substitution_cached(
382 &self,
383 term: TermId,
384 subst: &FxHashMap<Spur, TermId>,
385 manager: &mut TermManager,
386 cache: &mut FxHashMap<TermId, TermId>,
387 ) -> TermId {
388 if let Some(&cached) = cache.get(&term) {
389 return cached;
390 }
391
392 let Some(t) = manager.get(term).cloned() else {
393 return term;
394 };
395
396 let result = match &t.kind {
397 TermKind::Var(name) => subst.get(name).copied().unwrap_or(term),
398 TermKind::Not(arg) => {
399 let new_arg = self.apply_substitution_cached(*arg, subst, manager, cache);
400 manager.mk_not(new_arg)
401 }
402 TermKind::And(args) => {
403 let new_args: Vec<_> = args
404 .iter()
405 .map(|&a| self.apply_substitution_cached(a, subst, manager, cache))
406 .collect();
407 manager.mk_and(new_args)
408 }
409 TermKind::Or(args) => {
410 let new_args: Vec<_> = args
411 .iter()
412 .map(|&a| self.apply_substitution_cached(a, subst, manager, cache))
413 .collect();
414 manager.mk_or(new_args)
415 }
416 TermKind::Implies(lhs, rhs) => {
417 let new_lhs = self.apply_substitution_cached(*lhs, subst, manager, cache);
418 let new_rhs = self.apply_substitution_cached(*rhs, subst, manager, cache);
419 manager.mk_implies(new_lhs, new_rhs)
420 }
421 TermKind::Eq(lhs, rhs) => {
422 let new_lhs = self.apply_substitution_cached(*lhs, subst, manager, cache);
423 let new_rhs = self.apply_substitution_cached(*rhs, subst, manager, cache);
424 manager.mk_eq(new_lhs, new_rhs)
425 }
426 TermKind::Lt(lhs, rhs) => {
427 let new_lhs = self.apply_substitution_cached(*lhs, subst, manager, cache);
428 let new_rhs = self.apply_substitution_cached(*rhs, subst, manager, cache);
429 manager.mk_lt(new_lhs, new_rhs)
430 }
431 TermKind::Le(lhs, rhs) => {
432 let new_lhs = self.apply_substitution_cached(*lhs, subst, manager, cache);
433 let new_rhs = self.apply_substitution_cached(*rhs, subst, manager, cache);
434 manager.mk_le(new_lhs, new_rhs)
435 }
436 TermKind::Gt(lhs, rhs) => {
437 let new_lhs = self.apply_substitution_cached(*lhs, subst, manager, cache);
438 let new_rhs = self.apply_substitution_cached(*rhs, subst, manager, cache);
439 manager.mk_gt(new_lhs, new_rhs)
440 }
441 TermKind::Ge(lhs, rhs) => {
442 let new_lhs = self.apply_substitution_cached(*lhs, subst, manager, cache);
443 let new_rhs = self.apply_substitution_cached(*rhs, subst, manager, cache);
444 manager.mk_ge(new_lhs, new_rhs)
445 }
446 TermKind::Add(args) => {
447 let new_args: SmallVec<[TermId; 4]> = args
448 .iter()
449 .map(|&a| self.apply_substitution_cached(a, subst, manager, cache))
450 .collect();
451 manager.mk_add(new_args)
452 }
453 TermKind::Sub(lhs, rhs) => {
454 let new_lhs = self.apply_substitution_cached(*lhs, subst, manager, cache);
455 let new_rhs = self.apply_substitution_cached(*rhs, subst, manager, cache);
456 manager.mk_sub(new_lhs, new_rhs)
457 }
458 TermKind::Mul(args) => {
459 let new_args: SmallVec<[TermId; 4]> = args
460 .iter()
461 .map(|&a| self.apply_substitution_cached(a, subst, manager, cache))
462 .collect();
463 manager.mk_mul(new_args)
464 }
465 TermKind::Div(lhs, rhs) => {
466 let new_lhs = self.apply_substitution_cached(*lhs, subst, manager, cache);
467 let new_rhs = self.apply_substitution_cached(*rhs, subst, manager, cache);
468 manager.mk_div(new_lhs, new_rhs)
469 }
470 TermKind::Mod(lhs, rhs) => {
471 let new_lhs = self.apply_substitution_cached(*lhs, subst, manager, cache);
472 let new_rhs = self.apply_substitution_cached(*rhs, subst, manager, cache);
473 manager.mk_mod(new_lhs, new_rhs)
474 }
475 TermKind::Neg(arg) => {
476 let new_arg = self.apply_substitution_cached(*arg, subst, manager, cache);
477 manager.mk_neg(new_arg)
478 }
479 TermKind::Ite(cond, then_br, else_br) => {
480 let new_cond = self.apply_substitution_cached(*cond, subst, manager, cache);
481 let new_then = self.apply_substitution_cached(*then_br, subst, manager, cache);
482 let new_else = self.apply_substitution_cached(*else_br, subst, manager, cache);
483 manager.mk_ite(new_cond, new_then, new_else)
484 }
485 TermKind::Apply { func, args } => {
486 let func_name = manager.resolve_str(*func).to_string();
487 let new_args: SmallVec<[TermId; 4]> = args
488 .iter()
489 .map(|&a| self.apply_substitution_cached(a, subst, manager, cache))
490 .collect();
491 manager.mk_apply(&func_name, new_args, t.sort)
492 }
493 TermKind::Select(array, index) => {
497 let new_array = self.apply_substitution_cached(*array, subst, manager, cache);
498 let new_index = self.apply_substitution_cached(*index, subst, manager, cache);
499 manager.mk_select(new_array, new_index)
500 }
501 TermKind::Store(array, index, value) => {
503 let new_array = self.apply_substitution_cached(*array, subst, manager, cache);
504 let new_index = self.apply_substitution_cached(*index, subst, manager, cache);
505 let new_value = self.apply_substitution_cached(*value, subst, manager, cache);
506 manager.mk_store(new_array, new_index, new_value)
507 }
508 _ => term,
510 };
511
512 cache.insert(term, result);
513 result
514 }
515
516 pub fn clear_dedup_cache(&mut self) {
520 self.generated_instantiations.clear();
521 }
522
523 fn is_duplicate(&self, inst: &Instantiation) -> bool {
525 let key = InstantiationKey::from(inst);
526 self.generated_instantiations.contains_key(&key)
527 }
528
529 fn record_instantiation(&mut self, inst: &Instantiation) {
531 let key = InstantiationKey::from(inst);
532 let count = self.generated_instantiations.entry(key).or_insert(0);
533 *count += 1;
534 self.stats.total_instantiations += 1;
535 self.stats.unique_instantiations = self.generated_instantiations.len();
536 }
537
538 fn check_timeout(&self) -> bool {
540 #[cfg(feature = "std")]
541 {
542 if let (Some(limit), Some(start)) = (self.time_limit, self.start_time) {
543 return start.elapsed() >= limit;
544 }
545 }
546 false
547 }
548
549 fn update_final_stats(&mut self) {
551 #[cfg(feature = "std")]
552 if let Some(start) = self.start_time {
553 self.stats.total_time_us = start.elapsed().as_micros() as u64;
554 }
555 }
556
557 pub fn clear(&mut self) {
559 self.quantifiers.clear();
560 self.generated_instantiations.clear();
561 self.extra_candidates.clear();
562 self.blind_attempted = false;
563 self.current_round = 0;
564 self.budget = MBQIBudget::new(self.budget.global_budget);
565 self.conflict_scores = ConflictScores::new(self.conflict_scores.decay_factor);
566 #[cfg(feature = "std")]
567 {
568 self.start_time = None;
569 }
570 self.instantiation_engine.clear_caches();
571 self.lazy_instantiator.clear();
572 }
573
574 pub fn collect_ground_terms(&mut self, _term: TermId, _manager: &TermManager) {
576 }
579
580 pub fn check_with_model(
582 &mut self,
583 model: &FxHashMap<TermId, TermId>,
584 manager: &mut TermManager,
585 ) -> MBQIResult {
586 #[derive(Debug)]
588 struct NoOpCallback;
589 impl SolverCallback for NoOpCallback {
590 fn on_instantiation(&mut self, _: &Instantiation) {}
591 fn on_round_start(&mut self, _: usize) {}
592 fn on_round_end(&mut self, _: usize, _: &MBQIResult) {}
593 fn on_conflict(&mut self, _: TermId, _: &[TermId]) {}
594 fn should_stop(&self) -> bool {
595 false
596 }
597 }
598 let mut callback = NoOpCallback;
599 self.run(model, manager, &mut callback)
600 }
601
602 pub fn stats(&self) -> &MBQIStats {
604 &self.stats
605 }
606
607 pub fn set_max_rounds(&mut self, max: usize) {
609 self.max_rounds = max;
610 }
611
612 #[cfg(feature = "std")]
614 pub fn set_time_limit(&mut self, limit: Duration) {
615 self.time_limit = Some(limit);
616 }
617
618 pub fn add_candidate(&mut self, term: TermId, sort: SortId) {
623 self.extra_candidates.entry(sort).or_default().push(term);
624 }
625
626 pub fn blind_tried(&self) -> bool {
628 self.blind_attempted
629 }
630
631 pub fn mark_blind_tried(&mut self) {
633 self.blind_attempted = true;
634 }
635
636 pub fn is_enabled(&self) -> bool {
638 true
640 }
641
642 pub fn register_declared_const(&mut self, term: TermId, sort: SortId) {
648 self.add_candidate(term, sort);
649 }
650
651 pub fn try_trivial_instantiation(&mut self, _manager: &mut TermManager) -> Vec<Instantiation> {
660 Vec::new()
661 }
662
663 pub fn generate_finite_domain_instantiations(
673 &mut self,
674 manager: &mut TermManager,
675 ) -> Vec<Instantiation> {
676 use num_bigint::BigInt;
677 let mut all_insts = Vec::new();
678 let quantifiers: Vec<_> = self.quantifiers.to_vec();
679
680 for quantifier in &quantifiers {
681 if !quantifier.is_universal || !quantifier.can_instantiate() {
682 continue;
683 }
684
685 let bounds = self.extract_variable_bounds(quantifier, manager);
687 if bounds.is_empty() {
688 continue;
689 }
690
691 let mut candidate_lists: Vec<Vec<TermId>> = Vec::new();
693 for &(var_name, sort) in &quantifier.bound_vars {
694 if let Some(&(lo, hi)) = bounds.get(&var_name) {
695 if hi - lo <= 20 && sort == manager.sorts.int_sort {
696 let cands: Vec<TermId> =
697 (lo..=hi).map(|v| manager.mk_int(BigInt::from(v))).collect();
698 candidate_lists.push(cands);
699 } else {
700 let mut cands = Vec::new();
702 if sort == manager.sorts.int_sort {
703 for i in -2i64..=5 {
704 cands.push(manager.mk_int(BigInt::from(i)));
705 }
706 }
707 candidate_lists.push(cands);
708 }
709 } else {
710 let mut cands = Vec::new();
711 if sort == manager.sorts.int_sort {
712 for i in -2i64..=5 {
713 cands.push(manager.mk_int(BigInt::from(i)));
714 }
715 }
716 candidate_lists.push(cands);
717 }
718 }
719
720 if candidate_lists.is_empty() || candidate_lists.iter().any(|c| c.is_empty()) {
721 continue;
722 }
723 let combos = self.enumerate_combinations_blind(&candidate_lists, 2000);
724 for combo in combos {
725 let mut subst = FxHashMap::default();
726 for (i, &val) in combo.iter().enumerate() {
727 if let Some(var_name) = quantifier.var_name(i) {
728 subst.insert(var_name, val);
729 }
730 }
731 let ground_body = self.apply_substitution(quantifier.body, &subst, manager);
732 let inst = Instantiation::new(
733 quantifier.term,
734 subst,
735 ground_body,
736 self.current_round as u32,
737 );
738 if !self.is_duplicate(&inst) {
739 self.record_instantiation(&inst);
740 all_insts.push(inst);
741 }
742 }
743 }
744 all_insts
745 }
746
747 fn extract_variable_bounds(
752 &self,
753 quantifier: &QuantifiedFormula,
754 manager: &TermManager,
755 ) -> FxHashMap<Spur, (i64, i64)> {
756 use num_traits::ToPrimitive;
757 let mut bounds: FxHashMap<Spur, (i64, i64)> = FxHashMap::default();
758 let Some(t) = manager.get(quantifier.body) else {
759 return bounds;
760 };
761
762 let guard = match &t.kind {
764 TermKind::Implies(guard, _) => *guard,
765 _ => return bounds,
766 };
767
768 let Some(gt) = manager.get(guard) else {
769 return bounds;
770 };
771
772 let args = match >.kind {
773 TermKind::And(args) => args.clone(),
774 _ => return bounds,
775 };
776
777 let mut lowers: FxHashMap<Spur, i64> = FxHashMap::default();
779 let mut uppers: FxHashMap<Spur, i64> = FxHashMap::default();
780
781 for &a in args.iter() {
782 let Some(at) = manager.get(a) else { continue };
783 match &at.kind {
784 TermKind::Ge(lhs, rhs) => {
785 if let (Some(lt), Some(rt)) = (manager.get(*lhs), manager.get(*rhs)) {
786 if let (TermKind::Var(name), TermKind::IntConst(n)) = (<.kind, &rt.kind) {
787 if let Some(v) = n.to_i64() {
788 lowers
789 .entry(*name)
790 .and_modify(|e| *e = (*e).max(v))
791 .or_insert(v);
792 }
793 }
794 }
795 }
796 TermKind::Le(lhs, rhs) => {
797 if let (Some(lt), Some(rt)) = (manager.get(*lhs), manager.get(*rhs)) {
798 if let (TermKind::Var(name), TermKind::IntConst(n)) = (<.kind, &rt.kind) {
799 if let Some(v) = n.to_i64() {
800 uppers
801 .entry(*name)
802 .and_modify(|e| *e = (*e).min(v))
803 .or_insert(v);
804 }
805 }
806 }
807 }
808 _ => {}
809 }
810 }
811
812 for (&name, &lo) in &lowers {
813 if let Some(&hi) = uppers.get(&name) {
814 if hi >= lo {
815 bounds.insert(name, (lo, hi));
816 }
817 }
818 }
819
820 bounds
821 }
822
823 pub fn generate_blind_instantiations(
827 &mut self,
828 manager: &mut TermManager,
829 ) -> Vec<Instantiation> {
830 use num_bigint::BigInt;
831
832 let mut all_insts = Vec::new();
833 let quantifiers: Vec<_> = self.quantifiers.to_vec();
834
835 for quantifier in &quantifiers {
836 if !quantifier.is_universal || !quantifier.can_instantiate() {
837 continue;
838 }
839
840 let mut candidate_lists: Vec<Vec<TermId>> = Vec::new();
842 for &(_var_name, sort) in &quantifier.bound_vars {
843 let mut cands = Vec::new();
844
845 if let Some(extras) = self.extra_candidates.get(&sort) {
847 for &t in extras {
848 if !cands.contains(&t) {
849 cands.push(t);
850 }
851 }
852 }
853
854 if sort == manager.sorts.int_sort {
856 for i in -2i64..=5 {
857 let val = manager.mk_int(BigInt::from(i));
858 if !cands.contains(&val) {
859 cands.push(val);
860 }
861 }
862 } else if sort == manager.sorts.bool_sort {
863 let t_val = manager.mk_true();
864 let f_val = manager.mk_false();
865 if !cands.contains(&t_val) {
866 cands.push(t_val);
867 }
868 if !cands.contains(&f_val) {
869 cands.push(f_val);
870 }
871 }
872
873 cands.truncate(16);
875 candidate_lists.push(cands);
876 }
877
878 if candidate_lists.is_empty() {
879 continue;
880 }
881
882 let combos = self.enumerate_combinations_blind(&candidate_lists, 500);
884 for combo in combos {
885 let mut subst = FxHashMap::default();
887 for (i, &val) in combo.iter().enumerate() {
888 if let Some(var_name) = quantifier.var_name(i) {
889 subst.insert(var_name, val);
890 }
891 }
892
893 let ground_body = self.apply_substitution(quantifier.body, &subst, manager);
894 let simplified = self.deep_simplify(ground_body, manager);
897
898 if manager
900 .get(simplified)
901 .is_some_and(|t| matches!(t.kind, TermKind::True))
902 {
903 continue;
904 }
905
906 if manager
913 .get(simplified)
914 .is_some_and(|t| matches!(t.kind, TermKind::Implies(_, _)))
915 {
916 continue;
917 }
918
919 let inst = Instantiation::new(quantifier.term, subst, simplified, 0);
920
921 if !self.is_duplicate(&inst) {
922 self.record_instantiation(&inst);
923 all_insts.push(inst);
924 }
925 }
926 }
927
928 all_insts
929 }
930
931 pub fn deep_simplify(&self, term: TermId, manager: &mut TermManager) -> TermId {
934 let mut cache = FxHashMap::default();
935 self.deep_simplify_cached(term, manager, &mut cache)
936 }
937
938 fn deep_simplify_cached(
939 &self,
940 term: TermId,
941 manager: &mut TermManager,
942 cache: &mut FxHashMap<TermId, TermId>,
943 ) -> TermId {
944 if let Some(&c) = cache.get(&term) {
945 return c;
946 }
947 let Some(t) = manager.get(term).cloned() else {
948 return term;
949 };
950 let result = match &t.kind {
951 TermKind::True
952 | TermKind::False
953 | TermKind::IntConst(_)
954 | TermKind::RealConst(_)
955 | TermKind::BitVecConst { .. }
956 | TermKind::StringLit(_)
957 | TermKind::Var(_) => term,
958
959 TermKind::Not(a) => {
960 let sa = self.deep_simplify_cached(*a, manager, cache);
961 match manager.get(sa).map(|t2| &t2.kind) {
962 Some(TermKind::True) => manager.mk_false(),
963 Some(TermKind::False) => manager.mk_true(),
964 _ => manager.mk_not(sa),
965 }
966 }
967 TermKind::And(args) => {
968 let mut simplified = Vec::new();
969 for &a in args.iter() {
970 let sa = self.deep_simplify_cached(a, manager, cache);
971 match manager.get(sa).map(|t2| &t2.kind) {
972 Some(TermKind::False) => {
973 return {
974 let r = manager.mk_false();
975 cache.insert(term, r);
976 r
977 };
978 }
979 Some(TermKind::True) => { }
980 _ => simplified.push(sa),
981 }
982 }
983 if simplified.is_empty() {
984 manager.mk_true()
985 } else if simplified.len() == 1 {
986 simplified[0]
987 } else {
988 manager.mk_and(simplified)
989 }
990 }
991 TermKind::Or(args) => {
992 let mut simplified = Vec::new();
993 for &a in args.iter() {
994 let sa = self.deep_simplify_cached(a, manager, cache);
995 match manager.get(sa).map(|t2| &t2.kind) {
996 Some(TermKind::True) => {
997 return {
998 let r = manager.mk_true();
999 cache.insert(term, r);
1000 r
1001 };
1002 }
1003 Some(TermKind::False) => { }
1004 _ => simplified.push(sa),
1005 }
1006 }
1007 if simplified.is_empty() {
1008 manager.mk_false()
1009 } else if simplified.len() == 1 {
1010 simplified[0]
1011 } else {
1012 manager.mk_or(simplified)
1013 }
1014 }
1015 TermKind::Implies(lhs, rhs) => {
1016 let sl = self.deep_simplify_cached(*lhs, manager, cache);
1017 let sr = self.deep_simplify_cached(*rhs, manager, cache);
1018 match manager.get(sl).map(|t2| &t2.kind) {
1019 Some(TermKind::False) => manager.mk_true(),
1020 Some(TermKind::True) => sr,
1021 _ => match manager.get(sr).map(|t2| &t2.kind) {
1022 Some(TermKind::True) => manager.mk_true(),
1023 _ => manager.mk_implies(sl, sr),
1024 },
1025 }
1026 }
1027 TermKind::Eq(lhs, rhs) => {
1028 let sl = self.deep_simplify_cached(*lhs, manager, cache);
1029 let sr = self.deep_simplify_cached(*rhs, manager, cache);
1030 self.simplify_eq(sl, sr, manager)
1031 }
1032 TermKind::Le(lhs, rhs) => {
1033 let sl = self.deep_simplify_cached(*lhs, manager, cache);
1034 let sr = self.deep_simplify_cached(*rhs, manager, cache);
1035 self.simplify_le(sl, sr, manager)
1036 }
1037 TermKind::Lt(lhs, rhs) => {
1038 let sl = self.deep_simplify_cached(*lhs, manager, cache);
1039 let sr = self.deep_simplify_cached(*rhs, manager, cache);
1040 self.simplify_lt(sl, sr, manager)
1041 }
1042 TermKind::Ge(lhs, rhs) => {
1043 let sl = self.deep_simplify_cached(*lhs, manager, cache);
1044 let sr = self.deep_simplify_cached(*rhs, manager, cache);
1045 self.simplify_le(sr, sl, manager) }
1047 TermKind::Gt(lhs, rhs) => {
1048 let sl = self.deep_simplify_cached(*lhs, manager, cache);
1049 let sr = self.deep_simplify_cached(*rhs, manager, cache);
1050 self.simplify_lt(sr, sl, manager) }
1052 TermKind::Select(arr, idx) => {
1053 let sa = self.deep_simplify_cached(*arr, manager, cache);
1054 let si = self.deep_simplify_cached(*idx, manager, cache);
1055 manager.mk_select(sa, si)
1056 }
1057 TermKind::Apply { func, args } => {
1058 let fname = manager.resolve_str(*func).to_string();
1059 let new_args: SmallVec<[TermId; 4]> = args
1060 .iter()
1061 .map(|&a| self.deep_simplify_cached(a, manager, cache))
1062 .collect();
1063 manager.mk_apply(&fname, new_args, t.sort)
1064 }
1065 _ => term,
1066 };
1067 cache.insert(term, result);
1068 result
1069 }
1070
1071 fn simplify_eq(&self, lhs: TermId, rhs: TermId, manager: &mut TermManager) -> TermId {
1073 if lhs == rhs {
1074 return manager.mk_true();
1075 }
1076 let l = manager.get(lhs).cloned();
1077 let r = manager.get(rhs).cloned();
1078 if let (Some(lt), Some(rt)) = (l, r) {
1079 if let (TermKind::IntConst(a), TermKind::IntConst(b)) = (<.kind, &rt.kind) {
1080 return if a == b {
1081 manager.mk_true()
1082 } else {
1083 manager.mk_false()
1084 };
1085 }
1086 }
1087 manager.mk_eq(lhs, rhs)
1088 }
1089
1090 fn simplify_le(&self, lhs: TermId, rhs: TermId, manager: &mut TermManager) -> TermId {
1092 let l = manager.get(lhs).cloned();
1093 let r = manager.get(rhs).cloned();
1094 if let (Some(lt), Some(rt)) = (l, r) {
1095 if let (TermKind::IntConst(a), TermKind::IntConst(b)) = (<.kind, &rt.kind) {
1096 return if a <= b {
1097 manager.mk_true()
1098 } else {
1099 manager.mk_false()
1100 };
1101 }
1102 }
1103 manager.mk_le(lhs, rhs)
1104 }
1105
1106 fn simplify_lt(&self, lhs: TermId, rhs: TermId, manager: &mut TermManager) -> TermId {
1108 let l = manager.get(lhs).cloned();
1109 let r = manager.get(rhs).cloned();
1110 if let (Some(lt), Some(rt)) = (l, r) {
1111 if let (TermKind::IntConst(a), TermKind::IntConst(b)) = (<.kind, &rt.kind) {
1112 return if a < b {
1113 manager.mk_true()
1114 } else {
1115 manager.mk_false()
1116 };
1117 }
1118 }
1119 manager.mk_lt(lhs, rhs)
1120 }
1121
1122 fn enumerate_combinations_blind(
1124 &self,
1125 candidates: &[Vec<TermId>],
1126 max_total: usize,
1127 ) -> Vec<Vec<TermId>> {
1128 if candidates.is_empty() {
1129 return vec![vec![]];
1130 }
1131
1132 let mut results = Vec::new();
1133 let mut indices = vec![0usize; candidates.len()];
1134
1135 loop {
1136 let combo: Vec<TermId> = indices
1137 .iter()
1138 .enumerate()
1139 .filter_map(|(i, &idx)| candidates.get(i).and_then(|c| c.get(idx).copied()))
1140 .collect();
1141
1142 if combo.len() == candidates.len() {
1143 results.push(combo);
1144 }
1145
1146 if results.len() >= max_total {
1147 break;
1148 }
1149
1150 let mut carry = true;
1152 for (i, idx) in indices.iter_mut().enumerate() {
1153 if carry {
1154 *idx += 1;
1155 let limit = candidates.get(i).map_or(1, |c| c.len());
1156 if *idx >= limit {
1157 *idx = 0;
1158 } else {
1159 carry = false;
1160 }
1161 }
1162 }
1163
1164 if carry {
1165 break;
1166 }
1167 }
1168
1169 results
1170 }
1171}
1172
1173impl Default for MBQIIntegration {
1174 fn default() -> Self {
1175 Self::new()
1176 }
1177}
1178
1179#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1181struct InstantiationKey {
1182 quantifier: TermId,
1183 binding: Vec<(oxiz_core::interner::Spur, TermId)>,
1184}
1185
1186impl From<&Instantiation> for InstantiationKey {
1187 fn from(inst: &Instantiation) -> Self {
1188 let mut binding: Vec<_> = inst.substitution.iter().map(|(&k, &v)| (k, v)).collect();
1189 binding.sort_by_key(|(k, _)| *k);
1190 Self {
1191 quantifier: inst.quantifier,
1192 binding,
1193 }
1194 }
1195}
1196
1197#[derive(Debug)]
1199pub struct DefaultCallback {
1200 stop_requested: bool,
1201}
1202
1203impl DefaultCallback {
1204 pub fn new() -> Self {
1205 Self {
1206 stop_requested: false,
1207 }
1208 }
1209
1210 pub fn request_stop(&mut self) {
1211 self.stop_requested = true;
1212 }
1213}
1214
1215impl Default for DefaultCallback {
1216 fn default() -> Self {
1217 Self::new()
1218 }
1219}
1220
1221impl SolverCallback for DefaultCallback {
1222 fn on_instantiation(&mut self, _inst: &Instantiation) {}
1223 fn on_conflict(&mut self, _quantifier: TermId, _reason: &[TermId]) {}
1224 fn on_round_start(&mut self, _round: usize) {}
1225 fn on_round_end(&mut self, _round: usize, _result: &MBQIResult) {}
1226 fn should_stop(&self) -> bool {
1227 self.stop_requested
1228 }
1229}
1230
1231#[cfg(test)]
1232mod tests {
1233 use super::*;
1234
1235 #[test]
1236 fn test_mbqi_integration_creation() {
1237 let integration = MBQIIntegration::new();
1238 assert_eq!(integration.quantifiers.len(), 0);
1239 assert_eq!(integration.current_round, 0);
1240 }
1241
1242 #[test]
1243 fn test_default_callback() {
1244 let mut callback = DefaultCallback::new();
1245 assert!(!callback.should_stop());
1246 callback.request_stop();
1247 assert!(callback.should_stop());
1248 }
1249
1250 #[test]
1251 fn test_integration_clear() {
1252 let mut integration = MBQIIntegration::new();
1253 integration.current_round = 5;
1254 integration.clear();
1255 assert_eq!(integration.current_round, 0);
1256 assert_eq!(integration.quantifiers.len(), 0);
1257 }
1258
1259 #[test]
1260 fn test_set_max_rounds() {
1261 let mut integration = MBQIIntegration::new();
1262 integration.set_max_rounds(50);
1263 assert_eq!(integration.max_rounds, 50);
1264 }
1265
1266 #[test]
1267 fn test_set_time_limit() {
1268 let mut integration = MBQIIntegration::new();
1269 let limit = Duration::from_secs(30);
1270 integration.set_time_limit(limit);
1271 assert_eq!(integration.time_limit, Some(limit));
1272 }
1273}