1#![allow(missing_docs)]
10
11#[allow(unused_imports)]
12use crate::prelude::*;
13use num_bigint::BigInt;
14use oxiz_core::ast::{TermId, TermKind, TermManager};
15use oxiz_core::interner::Spur;
16use oxiz_core::sort::SortId;
17use smallvec::SmallVec;
18
19use super::counterexample::CounterExampleGenerator;
20use super::model_completion::CompletedModel;
21use super::{Instantiation, InstantiationReason, QuantifiedFormula};
22
23#[derive(Debug)]
25pub struct InstantiationContext {
26 pub manager: TermManager,
28 pub model: CompletedModel,
30 pub generation: u32,
32 pub equalities: FxHashMap<TermId, TermId>,
34}
35
36impl InstantiationContext {
37 pub fn new(manager: TermManager) -> Self {
39 Self {
40 manager,
41 model: CompletedModel::new(),
42 generation: 0,
43 equalities: FxHashMap::default(),
44 }
45 }
46
47 pub fn set_model(&mut self, model: CompletedModel) {
49 self.model = model;
50 }
51
52 pub fn next_generation(&mut self) {
54 self.generation += 1;
55 }
56
57 pub fn add_equality(&mut self, lhs: TermId, rhs: TermId) {
59 self.equalities.insert(lhs, rhs);
60 self.equalities.insert(rhs, lhs);
61 }
62
63 pub fn find_representative(&self, term: TermId) -> TermId {
65 let mut current = term;
66 let mut visited = FxHashSet::default();
67
68 while let Some(&next) = self.equalities.get(¤t) {
69 if visited.contains(&next) {
70 break; }
72 visited.insert(current);
73 current = next;
74 }
75
76 current
77 }
78}
79
80#[derive(Debug, Clone)]
82pub struct InstantiationPattern {
83 pub terms: Vec<TermId>,
85 pub vars: FxHashSet<Spur>,
87 pub num_vars: usize,
89 pub quality: f64,
91}
92
93impl InstantiationPattern {
94 pub fn new(terms: Vec<TermId>) -> Self {
96 Self {
97 terms,
98 vars: FxHashSet::default(),
99 num_vars: 0,
100 quality: 1.0,
101 }
102 }
103
104 pub fn extract_patterns(quantifier: &QuantifiedFormula, manager: &TermManager) -> Vec<Self> {
106 let mut patterns = Vec::new();
107
108 if !quantifier.patterns.is_empty() {
110 for pattern_terms in &quantifier.patterns {
111 let mut pattern = Self::new(pattern_terms.clone());
112 pattern.collect_vars(manager);
113 pattern.calculate_quality(manager);
114 patterns.push(pattern);
115 }
116 } else {
117 let generated = Self::generate_patterns(quantifier.body, manager);
119 patterns.extend(generated);
120 }
121
122 patterns
123 }
124
125 fn generate_patterns(term: TermId, manager: &TermManager) -> Vec<Self> {
127 let mut patterns = Vec::new();
128 let candidates = Self::collect_pattern_candidates(term, manager);
129
130 for candidate in candidates {
131 let mut pattern = Self::new(vec![candidate]);
132 pattern.collect_vars(manager);
133 if pattern.num_vars > 0 {
134 pattern.calculate_quality(manager);
135 patterns.push(pattern);
136 }
137 }
138
139 patterns
140 }
141
142 fn collect_pattern_candidates(term: TermId, manager: &TermManager) -> Vec<TermId> {
144 let mut candidates = Vec::new();
145 let mut visited = FxHashSet::default();
146 Self::collect_candidates_rec(term, &mut candidates, &mut visited, manager);
147 candidates
148 }
149
150 fn collect_candidates_rec(
151 term: TermId,
152 candidates: &mut Vec<TermId>,
153 visited: &mut FxHashSet<TermId>,
154 manager: &TermManager,
155 ) {
156 if visited.contains(&term) {
157 return;
158 }
159 visited.insert(term);
160
161 let Some(t) = manager.get(term) else {
162 return;
163 };
164
165 if matches!(t.kind, TermKind::Apply { .. }) {
167 candidates.push(term);
168 }
169
170 match &t.kind {
172 TermKind::Apply { args, .. } => {
173 for &arg in args.iter() {
174 Self::collect_candidates_rec(arg, candidates, visited, manager);
175 }
176 }
177 TermKind::And(args) | TermKind::Or(args) => {
178 for &arg in args {
179 Self::collect_candidates_rec(arg, candidates, visited, manager);
180 }
181 }
182 TermKind::Eq(lhs, rhs) | TermKind::Lt(lhs, rhs) | TermKind::Le(lhs, rhs) => {
183 Self::collect_candidates_rec(*lhs, candidates, visited, manager);
184 Self::collect_candidates_rec(*rhs, candidates, visited, manager);
185 }
186 _ => {}
187 }
188 }
189
190 fn collect_vars(&mut self, manager: &TermManager) {
192 self.vars.clear();
193 let mut visited = FxHashSet::default();
194
195 let terms: Vec<_> = self.terms.to_vec();
197 for term in terms {
198 self.collect_vars_rec(term, &mut visited, manager);
199 }
200
201 self.num_vars = self.vars.len();
202 }
203
204 fn collect_vars_rec(
205 &mut self,
206 term: TermId,
207 visited: &mut FxHashSet<TermId>,
208 manager: &TermManager,
209 ) {
210 if visited.contains(&term) {
211 return;
212 }
213 visited.insert(term);
214
215 let Some(t) = manager.get(term) else {
216 return;
217 };
218
219 if let TermKind::Var(name) = t.kind {
220 self.vars.insert(name);
221 }
222
223 match &t.kind {
225 TermKind::Apply { args, .. } => {
226 for &arg in args.iter() {
227 self.collect_vars_rec(arg, visited, manager);
228 }
229 }
230 TermKind::Not(arg) | TermKind::Neg(arg) => {
231 self.collect_vars_rec(*arg, visited, manager);
232 }
233 TermKind::And(args) | TermKind::Or(args) => {
234 for &arg in args {
235 self.collect_vars_rec(arg, visited, manager);
236 }
237 }
238 _ => {}
239 }
240 }
241
242 fn calculate_quality(&mut self, manager: &TermManager) {
244 let var_factor = 1.0 + (self.num_vars as f64);
250 let term_factor = 1.0 / (1.0 + self.terms.len() as f64);
251 let func_factor = if self.has_function_applications(manager) {
252 2.0
253 } else {
254 1.0
255 };
256
257 self.quality = var_factor * term_factor * func_factor;
258 }
259
260 fn has_function_applications(&self, manager: &TermManager) -> bool {
261 for &term in &self.terms {
262 if let Some(t) = manager.get(term)
263 && matches!(t.kind, TermKind::Apply { .. })
264 {
265 return true;
266 }
267 }
268 false
269 }
270}
271
272#[derive(Debug)]
274pub struct QuantifierInstantiator {
275 cex_generator: CounterExampleGenerator,
277 dedup_cache: FxHashSet<InstantiationKey>,
279 depth_tracking: FxHashMap<TermId, u32>,
285 pub max_depth: u32,
287 stats: InstantiatorStats,
289}
290
291impl QuantifierInstantiator {
292 pub fn new() -> Self {
294 Self {
295 cex_generator: CounterExampleGenerator::new(),
296 dedup_cache: FxHashSet::default(),
297 depth_tracking: FxHashMap::default(),
298 max_depth: 0,
299 stats: InstantiatorStats::default(),
300 }
301 }
302
303 pub fn with_max_depth(max_depth: u32) -> Self {
305 Self {
306 cex_generator: CounterExampleGenerator::new(),
307 dedup_cache: FxHashSet::default(),
308 depth_tracking: FxHashMap::default(),
309 max_depth,
310 stats: InstantiatorStats::default(),
311 }
312 }
313
314 pub fn current_depth(&self, quantifier: TermId) -> u32 {
316 self.depth_tracking.get(&quantifier).copied().unwrap_or(0)
317 }
318
319 fn depth_allows(&self, quantifier: TermId) -> bool {
321 if self.max_depth == 0 {
322 return true; }
324 self.current_depth(quantifier) < self.max_depth
325 }
326
327 pub fn increment_depth(&mut self, quantifier: TermId) {
329 let entry = self.depth_tracking.entry(quantifier).or_insert(0);
330 *entry = entry.saturating_add(1);
331 }
332
333 pub fn instantiate_from_model(
335 &mut self,
336 quantifier: &QuantifiedFormula,
337 model: &CompletedModel,
338 manager: &mut TermManager,
339 ) -> Vec<Instantiation> {
340 self.stats.num_instantiation_attempts += 1;
341
342 if !self.depth_allows(quantifier.term) {
345 return Vec::new();
346 }
347
348 let mut instantiations = Vec::new();
349
350 let cex_result = self.cex_generator.generate(quantifier, model, manager);
352
353 for cex in cex_result.counterexamples {
355 if !self.depth_allows(quantifier.term) {
358 break;
359 }
360
361 let ground_body = self.apply_substitution(quantifier.body, &cex.assignment, manager);
363
364 let inst = cex.to_instantiation(ground_body);
365
366 if self.is_duplicate(&inst) {
368 self.stats.num_duplicates_filtered += 1;
369 continue;
370 }
371
372 self.record_instantiation(&inst);
373 self.increment_depth(quantifier.term);
374 instantiations.push(inst);
375 }
376
377 self.stats.num_instantiations_generated += instantiations.len();
378 instantiations
379 }
380
381 pub fn instantiate_from_conflict(
383 &mut self,
384 quantifier: &QuantifiedFormula,
385 conflict: &[TermId],
386 model: &CompletedModel,
387 manager: &mut TermManager,
388 ) -> Vec<Instantiation> {
389 if !self.depth_allows(quantifier.term) {
391 return Vec::new();
392 }
393
394 let mut instantiations = Vec::new();
395
396 let conflict_terms = self.extract_relevant_terms(conflict, manager);
398
399 for assignment in
401 self.build_assignments_from_terms(&quantifier.bound_vars, &conflict_terms, manager)
402 {
403 if !self.depth_allows(quantifier.term) {
404 break;
405 }
406
407 let ground_body = self.apply_substitution(quantifier.body, &assignment, manager);
408
409 let inst = Instantiation::with_reason(
410 quantifier.term,
411 assignment,
412 ground_body,
413 model.generation,
414 InstantiationReason::Conflict,
415 );
416
417 if !self.is_duplicate(&inst) {
418 self.record_instantiation(&inst);
419 self.increment_depth(quantifier.term);
420 instantiations.push(inst);
421 }
422 }
423
424 instantiations
425 }
426
427 fn apply_substitution(
429 &self,
430 term: TermId,
431 subst: &FxHashMap<Spur, TermId>,
432 manager: &mut TermManager,
433 ) -> TermId {
434 let mut cache = FxHashMap::default();
435 self.apply_substitution_cached(term, subst, manager, &mut cache)
436 }
437
438 fn apply_substitution_cached(
439 &self,
440 term: TermId,
441 subst: &FxHashMap<Spur, TermId>,
442 manager: &mut TermManager,
443 cache: &mut FxHashMap<TermId, TermId>,
444 ) -> TermId {
445 if let Some(&cached) = cache.get(&term) {
446 return cached;
447 }
448
449 let Some(t) = manager.get(term).cloned() else {
450 return term;
451 };
452
453 let result = match &t.kind {
454 TermKind::Var(name) => subst.get(name).copied().unwrap_or(term),
455 TermKind::Not(arg) => {
456 let new_arg = self.apply_substitution_cached(*arg, subst, manager, cache);
457 manager.mk_not(new_arg)
458 }
459 TermKind::And(args) => {
460 let new_args: Vec<_> = args
461 .iter()
462 .map(|&a| self.apply_substitution_cached(a, subst, manager, cache))
463 .collect();
464 manager.mk_and(new_args)
465 }
466 TermKind::Or(args) => {
467 let new_args: Vec<_> = args
468 .iter()
469 .map(|&a| self.apply_substitution_cached(a, subst, manager, cache))
470 .collect();
471 manager.mk_or(new_args)
472 }
473 TermKind::Eq(lhs, rhs) => {
474 let new_lhs = self.apply_substitution_cached(*lhs, subst, manager, cache);
475 let new_rhs = self.apply_substitution_cached(*rhs, subst, manager, cache);
476 manager.mk_eq(new_lhs, new_rhs)
477 }
478 TermKind::Lt(lhs, rhs) => {
479 let new_lhs = self.apply_substitution_cached(*lhs, subst, manager, cache);
480 let new_rhs = self.apply_substitution_cached(*rhs, subst, manager, cache);
481 manager.mk_lt(new_lhs, new_rhs)
482 }
483 TermKind::Le(lhs, rhs) => {
484 let new_lhs = self.apply_substitution_cached(*lhs, subst, manager, cache);
485 let new_rhs = self.apply_substitution_cached(*rhs, subst, manager, cache);
486 manager.mk_le(new_lhs, new_rhs)
487 }
488 TermKind::Add(args) => {
489 let new_args: SmallVec<[TermId; 4]> = args
490 .iter()
491 .map(|&a| self.apply_substitution_cached(a, subst, manager, cache))
492 .collect();
493 manager.mk_add(new_args)
494 }
495 TermKind::Mul(args) => {
496 let new_args: SmallVec<[TermId; 4]> = args
497 .iter()
498 .map(|&a| self.apply_substitution_cached(a, subst, manager, cache))
499 .collect();
500 manager.mk_mul(new_args)
501 }
502 TermKind::Apply { func, args } => {
503 let func_name = manager.resolve_str(*func).to_string();
504 let new_args: SmallVec<[TermId; 4]> = args
505 .iter()
506 .map(|&a| self.apply_substitution_cached(a, subst, manager, cache))
507 .collect();
508 manager.mk_apply(&func_name, new_args, t.sort)
509 }
510 TermKind::Select(array, index) => {
511 let new_array = self.apply_substitution_cached(*array, subst, manager, cache);
512 let new_index = self.apply_substitution_cached(*index, subst, manager, cache);
513 manager.mk_select(new_array, new_index)
514 }
515 TermKind::Store(array, index, value) => {
516 let new_array = self.apply_substitution_cached(*array, subst, manager, cache);
517 let new_index = self.apply_substitution_cached(*index, subst, manager, cache);
518 let new_value = self.apply_substitution_cached(*value, subst, manager, cache);
519 manager.mk_store(new_array, new_index, new_value)
520 }
521 _ => term,
526 };
527
528 cache.insert(term, result);
529 result
530 }
531
532 fn extract_relevant_terms(&self, conflict: &[TermId], manager: &TermManager) -> Vec<TermId> {
534 let mut terms = Vec::new();
535 let mut visited = FxHashSet::default();
536
537 for &term in conflict {
538 self.extract_relevant_terms_rec(term, &mut terms, &mut visited, manager);
539 }
540
541 terms
542 }
543
544 fn extract_relevant_terms_rec(
545 &self,
546 term: TermId,
547 terms: &mut Vec<TermId>,
548 visited: &mut FxHashSet<TermId>,
549 manager: &TermManager,
550 ) {
551 if visited.contains(&term) {
552 return;
553 }
554 visited.insert(term);
555
556 let Some(t) = manager.get(term) else {
557 return;
558 };
559
560 if self.is_ground_value(term, manager) {
562 terms.push(term);
563 }
564
565 match &t.kind {
567 TermKind::Not(arg) | TermKind::Neg(arg) => {
568 self.extract_relevant_terms_rec(*arg, terms, visited, manager);
569 }
570 TermKind::And(args) | TermKind::Or(args) => {
571 for &arg in args {
572 self.extract_relevant_terms_rec(arg, terms, visited, manager);
573 }
574 }
575 TermKind::Eq(lhs, rhs) | TermKind::Lt(lhs, rhs) => {
576 self.extract_relevant_terms_rec(*lhs, terms, visited, manager);
577 self.extract_relevant_terms_rec(*rhs, terms, visited, manager);
578 }
579 TermKind::Apply { args, .. } => {
580 for &arg in args.iter() {
581 self.extract_relevant_terms_rec(arg, terms, visited, manager);
582 }
583 }
584 _ => {}
585 }
586 }
587
588 fn is_ground_value(&self, term: TermId, manager: &TermManager) -> bool {
590 let Some(t) = manager.get(term) else {
591 return false;
592 };
593
594 matches!(
595 t.kind,
596 TermKind::True
597 | TermKind::False
598 | TermKind::IntConst(_)
599 | TermKind::RealConst(_)
600 | TermKind::BitVecConst { .. }
601 )
602 }
603
604 fn build_assignments_from_terms(
606 &self,
607 bound_vars: &[(Spur, SortId)],
608 terms: &[TermId],
609 manager: &TermManager,
610 ) -> Vec<FxHashMap<Spur, TermId>> {
611 let mut assignments = Vec::new();
612
613 let mut terms_by_sort: FxHashMap<SortId, Vec<TermId>> = FxHashMap::default();
615 for &term in terms {
616 if let Some(t) = manager.get(term) {
617 terms_by_sort.entry(t.sort).or_default().push(term);
618 }
619 }
620
621 let mut candidates = Vec::new();
623 for &(_name, sort) in bound_vars {
624 let sort_terms = terms_by_sort
625 .get(&sort)
626 .map(|v| v.as_slice())
627 .unwrap_or(&[]);
628 candidates.push(sort_terms.to_vec());
629 }
630
631 let max_combinations = 10;
633 let mut indices = vec![0usize; bound_vars.len()];
634
635 for _ in 0..max_combinations {
636 let mut assignment = FxHashMap::default();
637 let mut valid = true;
638
639 for (i, &idx) in indices.iter().enumerate() {
640 if let Some(cands) = candidates.get(i) {
641 if let Some(&term) = cands.get(idx) {
642 if let Some((name, _)) = bound_vars.get(i) {
643 assignment.insert(*name, term);
644 }
645 } else {
646 valid = false;
647 break;
648 }
649 }
650 }
651
652 if valid && assignment.len() == bound_vars.len() {
653 assignments.push(assignment);
654 }
655
656 let mut carry = true;
658 for (i, idx) in indices.iter_mut().enumerate() {
659 if carry {
660 *idx += 1;
661 let limit = candidates.get(i).map_or(1, |c| c.len());
662 if *idx >= limit {
663 *idx = 0;
664 } else {
665 carry = false;
666 }
667 }
668 }
669
670 if carry {
671 break;
672 }
673 }
674
675 assignments
676 }
677
678 fn is_duplicate(&self, inst: &Instantiation) -> bool {
680 let key = InstantiationKey::from_instantiation(inst);
681 self.dedup_cache.contains(&key)
682 }
683
684 fn record_instantiation(&mut self, inst: &Instantiation) {
686 let key = InstantiationKey::from_instantiation(inst);
687 self.dedup_cache.insert(key);
688 }
689
690 pub fn clear_cache(&mut self) {
692 self.dedup_cache.clear();
693 self.depth_tracking.clear();
694 self.cex_generator.clear_cache();
695 }
696
697 pub fn stats(&self) -> &InstantiatorStats {
699 &self.stats
700 }
701}
702
703impl Default for QuantifierInstantiator {
704 fn default() -> Self {
705 Self::new()
706 }
707}
708
709#[derive(Debug, Clone, PartialEq, Eq, Hash)]
711struct InstantiationKey {
712 quantifier: TermId,
713 binding: Vec<(Spur, TermId)>,
714}
715
716impl InstantiationKey {
717 fn from_instantiation(inst: &Instantiation) -> Self {
718 let mut binding: Vec<_> = inst.substitution.iter().map(|(&k, &v)| (k, v)).collect();
719 binding.sort_by_key(|(k, _)| *k);
720 Self {
721 quantifier: inst.quantifier,
722 binding,
723 }
724 }
725}
726
727#[derive(Debug)]
729pub struct InstantiationEngine {
730 instantiator: QuantifierInstantiator,
732 pattern_matcher: PatternMatcher,
734 enumerative: EnumerativeInstantiator,
736 stats: EngineStats,
738}
739
740impl InstantiationEngine {
741 pub fn new() -> Self {
743 Self {
744 instantiator: QuantifierInstantiator::new(),
745 pattern_matcher: PatternMatcher::new(),
746 enumerative: EnumerativeInstantiator::new(),
747 stats: EngineStats::default(),
748 }
749 }
750
751 pub fn instantiate(
753 &mut self,
754 quantifier: &QuantifiedFormula,
755 model: &CompletedModel,
756 manager: &mut TermManager,
757 ) -> Vec<Instantiation> {
758 let mut instantiations = Vec::new();
759
760 let model_based = self
762 .instantiator
763 .instantiate_from_model(quantifier, model, manager);
764 instantiations.extend(model_based);
765
766 if !quantifier.patterns.is_empty() {
768 let pattern_based = self
769 .pattern_matcher
770 .match_patterns(quantifier, model, manager);
771 instantiations.extend(pattern_based);
772 }
773
774 if instantiations.is_empty() {
776 let enumerative = self.enumerative.enumerate(quantifier, model, manager, 10);
777 instantiations.extend(enumerative);
778 }
779
780 self.stats.num_instantiations += instantiations.len();
781 instantiations
782 }
783
784 pub fn clear_caches(&mut self) {
786 self.instantiator.clear_cache();
787 self.pattern_matcher.clear_cache();
788 }
789
790 pub fn stats(&self) -> &EngineStats {
792 &self.stats
793 }
794}
795
796impl Default for InstantiationEngine {
797 fn default() -> Self {
798 Self::new()
799 }
800}
801
802#[derive(Debug)]
804struct PatternMatcher {
805 cache: FxHashMap<TermId, Vec<FxHashMap<Spur, TermId>>>,
807}
808
809impl PatternMatcher {
810 fn new() -> Self {
811 Self {
812 cache: FxHashMap::default(),
813 }
814 }
815
816 fn match_patterns(
817 &mut self,
818 quantifier: &QuantifiedFormula,
819 model: &CompletedModel,
820 manager: &mut TermManager,
821 ) -> Vec<Instantiation> {
822 let mut instantiations = Vec::new();
823
824 let patterns = InstantiationPattern::extract_patterns(quantifier, manager);
826
827 for pattern in patterns {
829 let matches = self.match_pattern(&pattern, model, manager);
830 for assignment in matches {
831 let ground_body = self.apply_substitution(quantifier.body, &assignment, manager);
832 let inst = Instantiation::with_reason(
833 quantifier.term,
834 assignment,
835 ground_body,
836 model.generation,
837 InstantiationReason::EMatching,
838 );
839 instantiations.push(inst);
840 }
841 }
842
843 instantiations
844 }
845
846 fn match_pattern(
847 &self,
848 _pattern: &InstantiationPattern,
849 _model: &CompletedModel,
850 _manager: &TermManager,
851 ) -> Vec<FxHashMap<Spur, TermId>> {
852 Vec::new()
855 }
856
857 fn apply_substitution(
858 &self,
859 term: TermId,
860 subst: &FxHashMap<Spur, TermId>,
861 manager: &mut TermManager,
862 ) -> TermId {
863 let instantiator = QuantifierInstantiator::new();
865 instantiator.apply_substitution(term, subst, manager)
866 }
867
868 fn clear_cache(&mut self) {
869 self.cache.clear();
870 }
871}
872
873#[derive(Debug)]
875struct EnumerativeInstantiator;
876
877impl EnumerativeInstantiator {
878 fn new() -> Self {
879 Self
880 }
881
882 fn enumerate(
883 &self,
884 quantifier: &QuantifiedFormula,
885 model: &CompletedModel,
886 manager: &mut TermManager,
887 max_per_var: usize,
888 ) -> Vec<Instantiation> {
889 let mut instantiations = Vec::new();
890
891 let domains = self.build_small_domains(&quantifier.bound_vars, model, manager, max_per_var);
893
894 let combinations = self.enumerate_combinations(&domains);
896
897 for combo in combinations {
898 let mut assignment = FxHashMap::default();
899 for (i, &value) in combo.iter().enumerate() {
900 if let Some((name, _)) = quantifier.bound_vars.get(i) {
901 assignment.insert(*name, value);
902 }
903 }
904
905 let instantiator = QuantifierInstantiator::new();
906 let ground_body =
907 instantiator.apply_substitution(quantifier.body, &assignment, manager);
908
909 let inst = Instantiation::with_reason(
910 quantifier.term,
911 assignment,
912 ground_body,
913 model.generation,
914 InstantiationReason::Enumerative,
915 );
916 instantiations.push(inst);
917 }
918
919 instantiations
920 }
921
922 fn build_small_domains(
923 &self,
924 bound_vars: &[(Spur, SortId)],
925 model: &CompletedModel,
926 manager: &mut TermManager,
927 max_per_var: usize,
928 ) -> Vec<Vec<TermId>> {
929 let mut domains = Vec::new();
930
931 for &(_name, sort) in bound_vars {
932 let mut domain = Vec::new();
933
934 if let Some(universe) = model.universe(sort) {
936 domain.extend_from_slice(universe);
937 }
938
939 if sort == manager.sorts.int_sort {
941 for i in -2i64..=5 {
942 let val = manager.mk_int(BigInt::from(i));
943 if !domain.contains(&val) {
944 domain.push(val);
945 }
946 }
947 } else if sort == manager.sorts.bool_sort {
948 domain.push(manager.mk_true());
949 domain.push(manager.mk_false());
950 }
951
952 domain.truncate(max_per_var);
953 domains.push(domain);
954 }
955
956 domains
957 }
958
959 fn enumerate_combinations(&self, domains: &[Vec<TermId>]) -> Vec<Vec<TermId>> {
960 if domains.is_empty() {
961 return vec![vec![]];
962 }
963
964 let mut results = Vec::new();
965 let mut indices = vec![0usize; domains.len()];
966 let max_results = 100; loop {
969 let combo: Vec<TermId> = indices
970 .iter()
971 .enumerate()
972 .filter_map(|(i, &idx)| domains.get(i).and_then(|d| d.get(idx).copied()))
973 .collect();
974
975 if combo.len() == domains.len() {
976 results.push(combo);
977 }
978
979 if results.len() >= max_results {
980 break;
981 }
982
983 let mut carry = true;
985 for (i, idx) in indices.iter_mut().enumerate() {
986 if carry {
987 *idx += 1;
988 let limit = domains.get(i).map_or(1, |d| d.len());
989 if *idx >= limit {
990 *idx = 0;
991 } else {
992 carry = false;
993 }
994 }
995 }
996
997 if carry {
998 break;
999 }
1000 }
1001
1002 results
1003 }
1004}
1005
1006#[derive(Debug, Clone, Default)]
1008pub struct InstantiatorStats {
1009 pub num_instantiation_attempts: usize,
1010 pub num_instantiations_generated: usize,
1011 pub num_duplicates_filtered: usize,
1012}
1013
1014#[derive(Debug, Clone, Default)]
1016pub struct EngineStats {
1017 pub num_instantiations: usize,
1018}
1019
1020#[cfg(test)]
1021mod tests {
1022 use super::*;
1023 use oxiz_core::interner::Key;
1024
1025 #[test]
1026 fn test_instantiation_context_creation() {
1027 let manager = TermManager::new();
1028 let ctx = InstantiationContext::new(manager);
1029 assert_eq!(ctx.generation, 0);
1030 }
1031
1032 #[test]
1033 fn test_instantiation_context_generation() {
1034 let manager = TermManager::new();
1035 let mut ctx = InstantiationContext::new(manager);
1036 ctx.next_generation();
1037 assert_eq!(ctx.generation, 1);
1038 }
1039
1040 #[test]
1041 fn test_instantiation_pattern_creation() {
1042 let pattern = InstantiationPattern::new(vec![TermId::new(1)]);
1043 assert_eq!(pattern.terms.len(), 1);
1044 assert_eq!(pattern.num_vars, 0);
1045 }
1046
1047 #[test]
1048 fn test_quantifier_instantiator_creation() {
1049 let inst = QuantifierInstantiator::new();
1050 assert_eq!(inst.stats.num_instantiation_attempts, 0);
1051 }
1052
1053 #[test]
1054 fn test_instantiation_key_equality() {
1055 let key1 = InstantiationKey {
1056 quantifier: TermId::new(1),
1057 binding: vec![(
1058 Spur::try_from_usize(1).expect("valid spur"),
1059 TermId::new(10),
1060 )],
1061 };
1062 let key2 = InstantiationKey {
1063 quantifier: TermId::new(1),
1064 binding: vec![(
1065 Spur::try_from_usize(1).expect("valid spur"),
1066 TermId::new(10),
1067 )],
1068 };
1069 assert_eq!(key1, key2);
1070 }
1071
1072 #[test]
1073 fn test_instantiation_engine_creation() {
1074 let engine = InstantiationEngine::new();
1075 assert_eq!(engine.stats.num_instantiations, 0);
1076 }
1077
1078 #[test]
1079 fn test_pattern_matcher_creation() {
1080 let matcher = PatternMatcher::new();
1081 assert_eq!(matcher.cache.len(), 0);
1082 }
1083
1084 #[test]
1085 fn test_enumerative_instantiator_creation() {
1086 let _enum_inst = EnumerativeInstantiator::new();
1087 }
1088}