1#![allow(missing_docs)]
10#![allow(dead_code)]
11
12use lasso::Spur;
13use num_bigint::BigInt;
14use oxiz_core::ast::{TermId, TermKind, TermManager};
15use oxiz_core::sort::SortId;
16use rustc_hash::{FxHashMap, FxHashSet};
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 stats: InstantiatorStats,
281}
282
283impl QuantifierInstantiator {
284 pub fn new() -> Self {
286 Self {
287 cex_generator: CounterExampleGenerator::new(),
288 dedup_cache: FxHashSet::default(),
289 stats: InstantiatorStats::default(),
290 }
291 }
292
293 pub fn instantiate_from_model(
295 &mut self,
296 quantifier: &QuantifiedFormula,
297 model: &CompletedModel,
298 manager: &mut TermManager,
299 ) -> Vec<Instantiation> {
300 self.stats.num_instantiation_attempts += 1;
301
302 let mut instantiations = Vec::new();
303
304 let counterexamples = self.cex_generator.generate(quantifier, model, manager);
306
307 for cex in counterexamples {
309 let ground_body = self.apply_substitution(quantifier.body, &cex.assignment, manager);
311
312 let inst = cex.to_instantiation(ground_body);
313
314 if self.is_duplicate(&inst) {
316 self.stats.num_duplicates_filtered += 1;
317 continue;
318 }
319
320 self.record_instantiation(&inst);
321 instantiations.push(inst);
322 }
323
324 self.stats.num_instantiations_generated += instantiations.len();
325 instantiations
326 }
327
328 pub fn instantiate_from_conflict(
330 &mut self,
331 quantifier: &QuantifiedFormula,
332 conflict: &[TermId],
333 model: &CompletedModel,
334 manager: &mut TermManager,
335 ) -> Vec<Instantiation> {
336 let mut instantiations = Vec::new();
337
338 let conflict_terms = self.extract_relevant_terms(conflict, manager);
340
341 for assignment in
343 self.build_assignments_from_terms(&quantifier.bound_vars, &conflict_terms, manager)
344 {
345 let ground_body = self.apply_substitution(quantifier.body, &assignment, manager);
346
347 let inst = Instantiation::with_reason(
348 quantifier.term,
349 assignment,
350 ground_body,
351 model.generation,
352 InstantiationReason::Conflict,
353 );
354
355 if !self.is_duplicate(&inst) {
356 self.record_instantiation(&inst);
357 instantiations.push(inst);
358 }
359 }
360
361 instantiations
362 }
363
364 fn apply_substitution(
366 &self,
367 term: TermId,
368 subst: &FxHashMap<Spur, TermId>,
369 manager: &mut TermManager,
370 ) -> TermId {
371 let mut cache = FxHashMap::default();
372 self.apply_substitution_cached(term, subst, manager, &mut cache)
373 }
374
375 fn apply_substitution_cached(
376 &self,
377 term: TermId,
378 subst: &FxHashMap<Spur, TermId>,
379 manager: &mut TermManager,
380 cache: &mut FxHashMap<TermId, TermId>,
381 ) -> TermId {
382 if let Some(&cached) = cache.get(&term) {
383 return cached;
384 }
385
386 let Some(t) = manager.get(term).cloned() else {
387 return term;
388 };
389
390 let result = match &t.kind {
391 TermKind::Var(name) => subst.get(name).copied().unwrap_or(term),
392 TermKind::Not(arg) => {
393 let new_arg = self.apply_substitution_cached(*arg, subst, manager, cache);
394 manager.mk_not(new_arg)
395 }
396 TermKind::And(args) => {
397 let new_args: Vec<_> = args
398 .iter()
399 .map(|&a| self.apply_substitution_cached(a, subst, manager, cache))
400 .collect();
401 manager.mk_and(new_args)
402 }
403 TermKind::Or(args) => {
404 let new_args: Vec<_> = args
405 .iter()
406 .map(|&a| self.apply_substitution_cached(a, subst, manager, cache))
407 .collect();
408 manager.mk_or(new_args)
409 }
410 TermKind::Eq(lhs, rhs) => {
411 let new_lhs = self.apply_substitution_cached(*lhs, subst, manager, cache);
412 let new_rhs = self.apply_substitution_cached(*rhs, subst, manager, cache);
413 manager.mk_eq(new_lhs, new_rhs)
414 }
415 TermKind::Lt(lhs, rhs) => {
416 let new_lhs = self.apply_substitution_cached(*lhs, subst, manager, cache);
417 let new_rhs = self.apply_substitution_cached(*rhs, subst, manager, cache);
418 manager.mk_lt(new_lhs, new_rhs)
419 }
420 TermKind::Le(lhs, rhs) => {
421 let new_lhs = self.apply_substitution_cached(*lhs, subst, manager, cache);
422 let new_rhs = self.apply_substitution_cached(*rhs, subst, manager, cache);
423 manager.mk_le(new_lhs, new_rhs)
424 }
425 TermKind::Add(args) => {
426 let new_args: SmallVec<[TermId; 4]> = args
427 .iter()
428 .map(|&a| self.apply_substitution_cached(a, subst, manager, cache))
429 .collect();
430 manager.mk_add(new_args)
431 }
432 TermKind::Mul(args) => {
433 let new_args: SmallVec<[TermId; 4]> = args
434 .iter()
435 .map(|&a| self.apply_substitution_cached(a, subst, manager, cache))
436 .collect();
437 manager.mk_mul(new_args)
438 }
439 TermKind::Apply { func, args } => {
440 let func_name = manager.resolve_str(*func).to_string();
441 let new_args: SmallVec<[TermId; 4]> = args
442 .iter()
443 .map(|&a| self.apply_substitution_cached(a, subst, manager, cache))
444 .collect();
445 manager.mk_apply(&func_name, new_args, t.sort)
446 }
447 _ => term,
448 };
449
450 cache.insert(term, result);
451 result
452 }
453
454 fn extract_relevant_terms(&self, conflict: &[TermId], manager: &TermManager) -> Vec<TermId> {
456 let mut terms = Vec::new();
457 let mut visited = FxHashSet::default();
458
459 for &term in conflict {
460 self.extract_relevant_terms_rec(term, &mut terms, &mut visited, manager);
461 }
462
463 terms
464 }
465
466 fn extract_relevant_terms_rec(
467 &self,
468 term: TermId,
469 terms: &mut Vec<TermId>,
470 visited: &mut FxHashSet<TermId>,
471 manager: &TermManager,
472 ) {
473 if visited.contains(&term) {
474 return;
475 }
476 visited.insert(term);
477
478 let Some(t) = manager.get(term) else {
479 return;
480 };
481
482 if self.is_ground_value(term, manager) {
484 terms.push(term);
485 }
486
487 match &t.kind {
489 TermKind::Not(arg) | TermKind::Neg(arg) => {
490 self.extract_relevant_terms_rec(*arg, terms, visited, manager);
491 }
492 TermKind::And(args) | TermKind::Or(args) => {
493 for &arg in args {
494 self.extract_relevant_terms_rec(arg, terms, visited, manager);
495 }
496 }
497 TermKind::Eq(lhs, rhs) | TermKind::Lt(lhs, rhs) => {
498 self.extract_relevant_terms_rec(*lhs, terms, visited, manager);
499 self.extract_relevant_terms_rec(*rhs, terms, visited, manager);
500 }
501 TermKind::Apply { args, .. } => {
502 for &arg in args.iter() {
503 self.extract_relevant_terms_rec(arg, terms, visited, manager);
504 }
505 }
506 _ => {}
507 }
508 }
509
510 fn is_ground_value(&self, term: TermId, manager: &TermManager) -> bool {
512 let Some(t) = manager.get(term) else {
513 return false;
514 };
515
516 matches!(
517 t.kind,
518 TermKind::True
519 | TermKind::False
520 | TermKind::IntConst(_)
521 | TermKind::RealConst(_)
522 | TermKind::BitVecConst { .. }
523 )
524 }
525
526 fn build_assignments_from_terms(
528 &self,
529 bound_vars: &[(Spur, SortId)],
530 terms: &[TermId],
531 manager: &TermManager,
532 ) -> Vec<FxHashMap<Spur, TermId>> {
533 let mut assignments = Vec::new();
534
535 let mut terms_by_sort: FxHashMap<SortId, Vec<TermId>> = FxHashMap::default();
537 for &term in terms {
538 if let Some(t) = manager.get(term) {
539 terms_by_sort.entry(t.sort).or_default().push(term);
540 }
541 }
542
543 let mut candidates = Vec::new();
545 for &(_name, sort) in bound_vars {
546 let sort_terms = terms_by_sort
547 .get(&sort)
548 .map(|v| v.as_slice())
549 .unwrap_or(&[]);
550 candidates.push(sort_terms.to_vec());
551 }
552
553 let max_combinations = 10;
555 let mut indices = vec![0usize; bound_vars.len()];
556
557 for _ in 0..max_combinations {
558 let mut assignment = FxHashMap::default();
559 let mut valid = true;
560
561 for (i, &idx) in indices.iter().enumerate() {
562 if let Some(cands) = candidates.get(i) {
563 if let Some(&term) = cands.get(idx) {
564 if let Some((name, _)) = bound_vars.get(i) {
565 assignment.insert(*name, term);
566 }
567 } else {
568 valid = false;
569 break;
570 }
571 }
572 }
573
574 if valid && assignment.len() == bound_vars.len() {
575 assignments.push(assignment);
576 }
577
578 let mut carry = true;
580 for (i, idx) in indices.iter_mut().enumerate() {
581 if carry {
582 *idx += 1;
583 let limit = candidates.get(i).map_or(1, |c| c.len());
584 if *idx >= limit {
585 *idx = 0;
586 } else {
587 carry = false;
588 }
589 }
590 }
591
592 if carry {
593 break;
594 }
595 }
596
597 assignments
598 }
599
600 fn is_duplicate(&self, inst: &Instantiation) -> bool {
602 let key = InstantiationKey::from_instantiation(inst);
603 self.dedup_cache.contains(&key)
604 }
605
606 fn record_instantiation(&mut self, inst: &Instantiation) {
608 let key = InstantiationKey::from_instantiation(inst);
609 self.dedup_cache.insert(key);
610 }
611
612 pub fn clear_cache(&mut self) {
614 self.dedup_cache.clear();
615 self.cex_generator.clear_cache();
616 }
617
618 pub fn stats(&self) -> &InstantiatorStats {
620 &self.stats
621 }
622}
623
624impl Default for QuantifierInstantiator {
625 fn default() -> Self {
626 Self::new()
627 }
628}
629
630#[derive(Debug, Clone, PartialEq, Eq, Hash)]
632struct InstantiationKey {
633 quantifier: TermId,
634 binding: Vec<(Spur, TermId)>,
635}
636
637impl InstantiationKey {
638 fn from_instantiation(inst: &Instantiation) -> Self {
639 let mut binding: Vec<_> = inst.substitution.iter().map(|(&k, &v)| (k, v)).collect();
640 binding.sort_by_key(|(k, _)| *k);
641 Self {
642 quantifier: inst.quantifier,
643 binding,
644 }
645 }
646}
647
648#[derive(Debug)]
650pub struct InstantiationEngine {
651 instantiator: QuantifierInstantiator,
653 pattern_matcher: PatternMatcher,
655 enumerative: EnumerativeInstantiator,
657 stats: EngineStats,
659}
660
661impl InstantiationEngine {
662 pub fn new() -> Self {
664 Self {
665 instantiator: QuantifierInstantiator::new(),
666 pattern_matcher: PatternMatcher::new(),
667 enumerative: EnumerativeInstantiator::new(),
668 stats: EngineStats::default(),
669 }
670 }
671
672 pub fn instantiate(
674 &mut self,
675 quantifier: &QuantifiedFormula,
676 model: &CompletedModel,
677 manager: &mut TermManager,
678 ) -> Vec<Instantiation> {
679 let mut instantiations = Vec::new();
680
681 let model_based = self
683 .instantiator
684 .instantiate_from_model(quantifier, model, manager);
685 instantiations.extend(model_based);
686
687 if !quantifier.patterns.is_empty() {
689 let pattern_based = self
690 .pattern_matcher
691 .match_patterns(quantifier, model, manager);
692 instantiations.extend(pattern_based);
693 }
694
695 if instantiations.is_empty() {
697 let enumerative = self.enumerative.enumerate(quantifier, model, manager, 3);
698 instantiations.extend(enumerative);
699 }
700
701 self.stats.num_instantiations += instantiations.len();
702 instantiations
703 }
704
705 pub fn clear_caches(&mut self) {
707 self.instantiator.clear_cache();
708 self.pattern_matcher.clear_cache();
709 }
710
711 pub fn stats(&self) -> &EngineStats {
713 &self.stats
714 }
715}
716
717impl Default for InstantiationEngine {
718 fn default() -> Self {
719 Self::new()
720 }
721}
722
723#[derive(Debug)]
725struct PatternMatcher {
726 cache: FxHashMap<TermId, Vec<FxHashMap<Spur, TermId>>>,
728}
729
730impl PatternMatcher {
731 fn new() -> Self {
732 Self {
733 cache: FxHashMap::default(),
734 }
735 }
736
737 fn match_patterns(
738 &mut self,
739 quantifier: &QuantifiedFormula,
740 model: &CompletedModel,
741 manager: &mut TermManager,
742 ) -> Vec<Instantiation> {
743 let mut instantiations = Vec::new();
744
745 let patterns = InstantiationPattern::extract_patterns(quantifier, manager);
747
748 for pattern in patterns {
750 let matches = self.match_pattern(&pattern, model, manager);
751 for assignment in matches {
752 let ground_body = self.apply_substitution(quantifier.body, &assignment, manager);
753 let inst = Instantiation::with_reason(
754 quantifier.term,
755 assignment,
756 ground_body,
757 model.generation,
758 InstantiationReason::EMatching,
759 );
760 instantiations.push(inst);
761 }
762 }
763
764 instantiations
765 }
766
767 fn match_pattern(
768 &self,
769 _pattern: &InstantiationPattern,
770 _model: &CompletedModel,
771 _manager: &TermManager,
772 ) -> Vec<FxHashMap<Spur, TermId>> {
773 Vec::new()
776 }
777
778 fn apply_substitution(
779 &self,
780 term: TermId,
781 subst: &FxHashMap<Spur, TermId>,
782 manager: &mut TermManager,
783 ) -> TermId {
784 let instantiator = QuantifierInstantiator::new();
786 instantiator.apply_substitution(term, subst, manager)
787 }
788
789 fn clear_cache(&mut self) {
790 self.cache.clear();
791 }
792}
793
794#[derive(Debug)]
796struct EnumerativeInstantiator;
797
798impl EnumerativeInstantiator {
799 fn new() -> Self {
800 Self
801 }
802
803 fn enumerate(
804 &self,
805 quantifier: &QuantifiedFormula,
806 model: &CompletedModel,
807 manager: &mut TermManager,
808 max_per_var: usize,
809 ) -> Vec<Instantiation> {
810 let mut instantiations = Vec::new();
811
812 let domains = self.build_small_domains(&quantifier.bound_vars, model, manager, max_per_var);
814
815 let combinations = self.enumerate_combinations(&domains);
817
818 for combo in combinations {
819 let mut assignment = FxHashMap::default();
820 for (i, &value) in combo.iter().enumerate() {
821 if let Some((name, _)) = quantifier.bound_vars.get(i) {
822 assignment.insert(*name, value);
823 }
824 }
825
826 let instantiator = QuantifierInstantiator::new();
827 let ground_body =
828 instantiator.apply_substitution(quantifier.body, &assignment, manager);
829
830 let inst = Instantiation::with_reason(
831 quantifier.term,
832 assignment,
833 ground_body,
834 model.generation,
835 InstantiationReason::Enumerative,
836 );
837 instantiations.push(inst);
838 }
839
840 instantiations
841 }
842
843 fn build_small_domains(
844 &self,
845 bound_vars: &[(Spur, SortId)],
846 model: &CompletedModel,
847 manager: &mut TermManager,
848 max_per_var: usize,
849 ) -> Vec<Vec<TermId>> {
850 let mut domains = Vec::new();
851
852 for &(_name, sort) in bound_vars {
853 let mut domain = Vec::new();
854
855 if let Some(universe) = model.universe(sort) {
857 domain.extend_from_slice(universe);
858 }
859
860 if sort == manager.sorts.int_sort {
862 for i in 0..max_per_var.min(3) {
863 domain.push(manager.mk_int(BigInt::from(i as i32)));
864 }
865 } else if sort == manager.sorts.bool_sort {
866 domain.push(manager.mk_true());
867 domain.push(manager.mk_false());
868 }
869
870 domain.truncate(max_per_var);
871 domains.push(domain);
872 }
873
874 domains
875 }
876
877 fn enumerate_combinations(&self, domains: &[Vec<TermId>]) -> Vec<Vec<TermId>> {
878 if domains.is_empty() {
879 return vec![vec![]];
880 }
881
882 let mut results = Vec::new();
883 let mut indices = vec![0usize; domains.len()];
884 let max_results = 100; loop {
887 let combo: Vec<TermId> = indices
888 .iter()
889 .enumerate()
890 .filter_map(|(i, &idx)| domains.get(i).and_then(|d| d.get(idx).copied()))
891 .collect();
892
893 if combo.len() == domains.len() {
894 results.push(combo);
895 }
896
897 if results.len() >= max_results {
898 break;
899 }
900
901 let mut carry = true;
903 for (i, idx) in indices.iter_mut().enumerate() {
904 if carry {
905 *idx += 1;
906 let limit = domains.get(i).map_or(1, |d| d.len());
907 if *idx >= limit {
908 *idx = 0;
909 } else {
910 carry = false;
911 }
912 }
913 }
914
915 if carry {
916 break;
917 }
918 }
919
920 results
921 }
922}
923
924#[derive(Debug, Clone, Default)]
926pub struct InstantiatorStats {
927 pub num_instantiation_attempts: usize,
928 pub num_instantiations_generated: usize,
929 pub num_duplicates_filtered: usize,
930}
931
932#[derive(Debug, Clone, Default)]
934pub struct EngineStats {
935 pub num_instantiations: usize,
936}
937
938#[cfg(test)]
939mod tests {
940 use super::*;
941 use lasso::Key;
942
943 #[test]
944 fn test_instantiation_context_creation() {
945 let manager = TermManager::new();
946 let ctx = InstantiationContext::new(manager);
947 assert_eq!(ctx.generation, 0);
948 }
949
950 #[test]
951 fn test_instantiation_context_generation() {
952 let manager = TermManager::new();
953 let mut ctx = InstantiationContext::new(manager);
954 ctx.next_generation();
955 assert_eq!(ctx.generation, 1);
956 }
957
958 #[test]
959 fn test_instantiation_pattern_creation() {
960 let pattern = InstantiationPattern::new(vec![TermId::new(1)]);
961 assert_eq!(pattern.terms.len(), 1);
962 assert_eq!(pattern.num_vars, 0);
963 }
964
965 #[test]
966 fn test_quantifier_instantiator_creation() {
967 let inst = QuantifierInstantiator::new();
968 assert_eq!(inst.stats.num_instantiation_attempts, 0);
969 }
970
971 #[test]
972 fn test_instantiation_key_equality() {
973 let key1 = InstantiationKey {
974 quantifier: TermId::new(1),
975 binding: vec![(
976 Spur::try_from_usize(1).expect("valid spur"),
977 TermId::new(10),
978 )],
979 };
980 let key2 = InstantiationKey {
981 quantifier: TermId::new(1),
982 binding: vec![(
983 Spur::try_from_usize(1).expect("valid spur"),
984 TermId::new(10),
985 )],
986 };
987 assert_eq!(key1, key2);
988 }
989
990 #[test]
991 fn test_instantiation_engine_creation() {
992 let engine = InstantiationEngine::new();
993 assert_eq!(engine.stats.num_instantiations, 0);
994 }
995
996 #[test]
997 fn test_pattern_matcher_creation() {
998 let matcher = PatternMatcher::new();
999 assert_eq!(matcher.cache.len(), 0);
1000 }
1001
1002 #[test]
1003 fn test_enumerative_instantiator_creation() {
1004 let _enum_inst = EnumerativeInstantiator::new();
1005 }
1006}