Skip to main content

oxiz_solver/mbqi/
instantiation.rs

1//! Model-Based Instantiation Engine
2//!
3//! This module implements the core instantiation logic for MBQI. It handles:
4//! - Extracting instantiations from models and counterexamples
5//! - Conflict-driven instantiation
6//! - Pattern matching and trigger selection
7//! - Instantiation deduplication and filtering
8
9#![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/// Context for instantiation
24#[derive(Debug)]
25pub struct InstantiationContext {
26    /// Term manager
27    pub manager: TermManager,
28    /// Current model
29    pub model: CompletedModel,
30    /// Generation counter
31    pub generation: u32,
32    /// E-graph for equality reasoning (simplified)
33    pub equalities: FxHashMap<TermId, TermId>,
34}
35
36impl InstantiationContext {
37    /// Create a new instantiation context
38    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    /// Set the current model
48    pub fn set_model(&mut self, model: CompletedModel) {
49        self.model = model;
50    }
51
52    /// Increment generation
53    pub fn next_generation(&mut self) {
54        self.generation += 1;
55    }
56
57    /// Add an equality
58    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    /// Find representative in equality graph
64    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(&current) {
69            if visited.contains(&next) {
70                break; // Cycle detected
71            }
72            visited.insert(current);
73            current = next;
74        }
75
76        current
77    }
78}
79
80/// Pattern for instantiation (E-matching style)
81#[derive(Debug, Clone)]
82pub struct InstantiationPattern {
83    /// Terms that form the pattern
84    pub terms: Vec<TermId>,
85    /// Variables that must be matched
86    pub vars: FxHashSet<Spur>,
87    /// Number of variables
88    pub num_vars: usize,
89    /// Pattern quality (higher = better)
90    pub quality: f64,
91}
92
93impl InstantiationPattern {
94    /// Create a new pattern
95    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    /// Extract patterns from a quantified formula
105    pub fn extract_patterns(quantifier: &QuantifiedFormula, manager: &TermManager) -> Vec<Self> {
106        let mut patterns = Vec::new();
107
108        // Use explicit patterns if available
109        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            // Auto-generate patterns from the body
118            let generated = Self::generate_patterns(quantifier.body, manager);
119            patterns.extend(generated);
120        }
121
122        patterns
123    }
124
125    /// Generate patterns from a term
126    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    /// Collect pattern candidates from a term
143    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        // Function applications are good pattern candidates
166        if matches!(t.kind, TermKind::Apply { .. }) {
167            candidates.push(term);
168        }
169
170        // Recurse into children
171        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    /// Collect variables in the pattern
191    fn collect_vars(&mut self, manager: &TermManager) {
192        self.vars.clear();
193        let mut visited = FxHashSet::default();
194
195        // Collect terms first to avoid borrow checker issues
196        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        // Recurse into children
224        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    /// Calculate pattern quality
243    fn calculate_quality(&mut self, manager: &TermManager) {
244        // Quality factors:
245        // - More variables = better (more specific)
246        // - Fewer terms = better (simpler)
247        // - Contains function applications = better
248
249        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/// Quantifier instantiator
273#[derive(Debug)]
274pub struct QuantifierInstantiator {
275    /// Counterexample generator
276    cex_generator: CounterExampleGenerator,
277    /// Deduplication cache
278    dedup_cache: FxHashSet<InstantiationKey>,
279    /// Per-quantifier instantiation depth tracking.
280    ///
281    /// When a quantifier is instantiated this counter is incremented.  If the
282    /// counter reaches `max_depth` (when `max_depth > 0`) further
283    /// instantiations of that quantifier are suppressed.
284    depth_tracking: FxHashMap<TermId, u32>,
285    /// Maximum allowed instantiation depth per quantifier (0 = unlimited).
286    pub max_depth: u32,
287    /// Statistics
288    stats: InstantiatorStats,
289}
290
291impl QuantifierInstantiator {
292    /// Create a new instantiator (no depth limit)
293    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    /// Create with a specified depth limit
304    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    /// Return the current depth for a quantifier (0 if not yet instantiated)
315    pub fn current_depth(&self, quantifier: TermId) -> u32 {
316        self.depth_tracking.get(&quantifier).copied().unwrap_or(0)
317    }
318
319    /// Check whether the depth limit permits another instantiation
320    fn depth_allows(&self, quantifier: TermId) -> bool {
321        if self.max_depth == 0 {
322            return true; // unlimited
323        }
324        self.current_depth(quantifier) < self.max_depth
325    }
326
327    /// Increment the depth counter for a quantifier after a successful instantiation.
328    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    /// Generate instantiations for a quantifier using model-based approach
334    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        // Depth-bound guard: skip entirely if this quantifier has already been
343        // instantiated to the maximum allowed depth.
344        if !self.depth_allows(quantifier.term) {
345            return Vec::new();
346        }
347
348        let mut instantiations = Vec::new();
349
350        // Generate counterexamples
351        let cex_result = self.cex_generator.generate(quantifier, model, manager);
352
353        // Convert counterexamples to instantiations
354        for cex in cex_result.counterexamples {
355            // Recheck depth inside the loop – a single MBQI round may produce
356            // many counterexamples but we still want to cap the total.
357            if !self.depth_allows(quantifier.term) {
358                break;
359            }
360
361            // Apply substitution to get ground instance
362            let ground_body = self.apply_substitution(quantifier.body, &cex.assignment, manager);
363
364            let inst = cex.to_instantiation(ground_body);
365
366            // Check for duplicates
367            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    /// Generate instantiations using conflict-driven approach
382    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        // Depth-bound guard
390        if !self.depth_allows(quantifier.term) {
391            return Vec::new();
392        }
393
394        let mut instantiations = Vec::new();
395
396        // Analyze the conflict to extract relevant terms
397        let conflict_terms = self.extract_relevant_terms(conflict, manager);
398
399        // Try to build instantiations from conflict terms
400        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    /// Apply substitution to a term
428    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            // Constants and other terms (Implies, Gt, Ge, Sub, Div, Mod,
522            // Neg, Ite) are not traversed here. The MBQIIntegration layer
523            // re-applies substitution for instantiation engine results using
524            // its own complete implementation.
525            _ => term,
526        };
527
528        cache.insert(term, result);
529        result
530    }
531
532    /// Extract relevant terms from conflict clause
533    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        // Collect ground terms
561        if self.is_ground_value(term, manager) {
562            terms.push(term);
563        }
564
565        // Recurse into children
566        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    /// Check if a term is a ground value
589    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    /// Build assignments from terms
605    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        // Group terms by sort
614        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        // Build candidate lists for each variable
622        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        // Enumerate combinations (limited)
632        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            // Increment indices
657            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    /// Check if an instantiation is a duplicate
679    fn is_duplicate(&self, inst: &Instantiation) -> bool {
680        let key = InstantiationKey::from_instantiation(inst);
681        self.dedup_cache.contains(&key)
682    }
683
684    /// Record an instantiation for deduplication
685    fn record_instantiation(&mut self, inst: &Instantiation) {
686        let key = InstantiationKey::from_instantiation(inst);
687        self.dedup_cache.insert(key);
688    }
689
690    /// Clear deduplication cache and reset depth counters
691    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    /// Get statistics
698    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/// Key for instantiation deduplication
710#[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/// Instantiation engine that coordinates all instantiation strategies
728#[derive(Debug)]
729pub struct InstantiationEngine {
730    /// Main quantifier instantiator
731    instantiator: QuantifierInstantiator,
732    /// Pattern matcher
733    pattern_matcher: PatternMatcher,
734    /// Enumerative instantiation
735    enumerative: EnumerativeInstantiator,
736    /// Statistics
737    stats: EngineStats,
738}
739
740impl InstantiationEngine {
741    /// Create a new instantiation engine
742    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    /// Generate instantiations for a quantifier
752    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        // Strategy 1: Model-based instantiation
761        let model_based = self
762            .instantiator
763            .instantiate_from_model(quantifier, model, manager);
764        instantiations.extend(model_based);
765
766        // Strategy 2: Pattern-based instantiation (if patterns exist)
767        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        // Strategy 3: Enumerative instantiation (as fallback)
775        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    /// Clear all caches
785    pub fn clear_caches(&mut self) {
786        self.instantiator.clear_cache();
787        self.pattern_matcher.clear_cache();
788    }
789
790    /// Get statistics
791    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/// Pattern matcher for E-matching style instantiation
803#[derive(Debug)]
804struct PatternMatcher {
805    /// Match cache
806    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        // Extract patterns
825        let patterns = InstantiationPattern::extract_patterns(quantifier, manager);
826
827        // Match each pattern
828        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        // Simplified pattern matching
853        // A full implementation would use E-matching algorithms
854        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        // Reuse implementation from QuantifierInstantiator
864        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/// Enumerative instantiator (brute-force small domain)
874#[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        // Build small domains for each variable
892        let domains = self.build_small_domains(&quantifier.bound_vars, model, manager, max_per_var);
893
894        // Enumerate all combinations
895        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            // Use universe if available
935            if let Some(universe) = model.universe(sort) {
936                domain.extend_from_slice(universe);
937            }
938
939            // Add default integer candidates from -2 to 5
940            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; // Limit total combinations
967
968        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            // Increment
984            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/// Statistics for instantiator
1007#[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/// Statistics for engine
1015#[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}