1#![allow(missing_docs)]
46#![allow(dead_code)]
47
48#[allow(unused_imports)]
49use crate::prelude::*;
50use core::fmt;
51use oxiz_core::ast::TermId;
52use oxiz_core::interner::Spur;
53use oxiz_core::sort::SortId;
54use smallvec::SmallVec;
55
56pub mod conflict_driven;
57pub mod counterexample;
58pub mod finite_model;
59pub mod heuristics;
60pub mod instantiation;
61pub mod integration;
62pub mod lazy_instantiation;
63pub mod macros;
64pub mod model_completion;
65pub mod patterns;
66
67pub use conflict_driven::{ConflictDrivenInstantiator, ConflictScores};
69pub use counterexample::{
70 CexGenerationResult, CounterExample, CounterExampleGenerator, RefinementStrategy,
71};
72pub use finite_model::{FiniteModel, FiniteModelFinder, SymmetryBreaker, UniverseSize};
73pub use heuristics::{
74 InstantiationHeuristic, MBQIBudget, MBQIHeuristics, MultiTriggerScorer, ScoredTriggerSet,
75 ScorerPolicy, SelectionStrategy, TriggerSelection, TriggerSet,
76};
77pub use instantiation::{
78 InstantiationContext, InstantiationEngine, InstantiationPattern, QuantifierInstantiator,
79};
80pub use integration::{MBQIIntegration, SolverCallback};
81pub use lazy_instantiation::{LazyInstantiator, LazyStrategy, MatchingContext};
82pub use model_completion::{
83 MacroSolver, ModelCompleter, ModelFixer, ProjectionFunction, UninterpretedSortHandler,
84};
85pub use patterns::{Pattern, PatternCoverScorer, PatternSet, PatternStrategy, TermShape};
86
87pub type QuantifierId = TermId;
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq)]
92pub struct QuantifierConfig {
93 pub pattern_strategy: PatternStrategy,
95}
96
97impl Default for QuantifierConfig {
98 fn default() -> Self {
99 Self {
100 pattern_strategy: PatternStrategy::GreedyCover,
101 }
102 }
103}
104
105#[derive(Debug, Clone)]
107pub struct QuantifiedFormula {
108 pub term: TermId,
110 pub bound_vars: SmallVec<[(Spur, SortId); 4]>,
112 pub body: TermId,
114 pub is_universal: bool,
116 pub nesting_depth: u32,
118 pub instantiation_count: usize,
120 pub max_instantiations: usize,
122 pub generation: u32,
124 pub weight: f64,
126 pub patterns: Vec<Vec<TermId>>,
128 pub is_flattened: bool,
130 pub specialized_body: Option<TermId>,
132}
133
134impl QuantifiedFormula {
135 pub fn new(
137 term: TermId,
138 bound_vars: SmallVec<[(Spur, SortId); 4]>,
139 body: TermId,
140 is_universal: bool,
141 ) -> Self {
142 Self {
143 term,
144 bound_vars,
145 body,
146 is_universal,
147 nesting_depth: 0,
148 instantiation_count: 0,
149 max_instantiations: 1000,
150 generation: 0,
151 weight: 1.0,
152 patterns: Vec::new(),
153 is_flattened: false,
154 specialized_body: None,
155 }
156 }
157
158 pub fn with_params(
160 term: TermId,
161 bound_vars: SmallVec<[(Spur, SortId); 4]>,
162 body: TermId,
163 is_universal: bool,
164 max_instantiations: usize,
165 weight: f64,
166 ) -> Self {
167 let mut qf = Self::new(term, bound_vars, body, is_universal);
168 qf.max_instantiations = max_instantiations;
169 qf.weight = weight;
170 qf
171 }
172
173 pub fn can_instantiate(&self) -> bool {
175 self.instantiation_count < self.max_instantiations
176 }
177
178 pub fn num_vars(&self) -> usize {
180 self.bound_vars.len()
181 }
182
183 pub fn var_name(&self, idx: usize) -> Option<Spur> {
185 self.bound_vars.get(idx).map(|(name, _)| *name)
186 }
187
188 pub fn var_sort(&self, idx: usize) -> Option<SortId> {
190 self.bound_vars.get(idx).map(|(_, sort)| *sort)
191 }
192
193 pub fn record_instantiation(&mut self) {
195 self.instantiation_count += 1;
196 }
197
198 pub fn priority_score(&self) -> f64 {
200 let inst_factor = 1.0 / (1.0 + self.instantiation_count as f64);
205 let depth_factor = 1.0 / (1.0 + self.nesting_depth as f64);
206 self.weight * inst_factor * depth_factor
207 }
208}
209
210#[derive(Debug, Clone)]
212pub struct Instantiation {
213 pub quantifier: TermId,
215 pub substitution: FxHashMap<Spur, TermId>,
217 pub result: TermId,
219 pub generation: u32,
221 pub reason: InstantiationReason,
223 pub cost: f64,
225}
226
227impl Instantiation {
228 pub fn new(
230 quantifier: TermId,
231 substitution: FxHashMap<Spur, TermId>,
232 result: TermId,
233 generation: u32,
234 ) -> Self {
235 Self {
236 quantifier,
237 substitution,
238 result,
239 generation,
240 reason: InstantiationReason::ModelBased,
241 cost: 1.0,
242 }
243 }
244
245 pub fn with_reason(
247 quantifier: TermId,
248 substitution: FxHashMap<Spur, TermId>,
249 result: TermId,
250 generation: u32,
251 reason: InstantiationReason,
252 ) -> Self {
253 Self {
254 quantifier,
255 substitution,
256 result,
257 generation,
258 reason,
259 cost: 1.0,
260 }
261 }
262
263 pub fn binding_key(&self) -> Vec<(Spur, TermId)> {
265 let mut key: Vec<_> = self.substitution.iter().map(|(&k, &v)| (k, v)).collect();
266 key.sort_by_key(|(name, _)| *name);
267 key
268 }
269}
270
271#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
273pub enum InstantiationReason {
274 ModelBased,
276 EMatching,
278 Conflict,
280 Enumerative,
282 User,
284 Theory,
286}
287
288impl fmt::Display for InstantiationReason {
289 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
290 match self {
291 Self::ModelBased => write!(f, "model-based"),
292 Self::EMatching => write!(f, "e-matching"),
293 Self::Conflict => write!(f, "conflict"),
294 Self::Enumerative => write!(f, "enumerative"),
295 Self::User => write!(f, "user"),
296 Self::Theory => write!(f, "theory"),
297 }
298 }
299}
300
301#[derive(Debug, Clone)]
303pub enum MBQIResult {
304 NoQuantifiers,
306 Satisfied,
308 NewInstantiations(Vec<Instantiation>),
310 Conflict {
312 quantifier: TermId,
313 reason: Vec<TermId>,
314 },
315 InstantiationLimit,
317 Unknown,
319}
320
321impl MBQIResult {
322 pub fn is_sat(&self) -> bool {
324 matches!(self, Self::Satisfied)
325 }
326
327 pub fn is_unsat(&self) -> bool {
329 matches!(self, Self::Conflict { .. })
330 }
331
332 pub fn has_instantiations(&self) -> bool {
334 matches!(self, Self::NewInstantiations(_))
335 }
336
337 pub fn num_instantiations(&self) -> usize {
339 match self {
340 Self::NewInstantiations(insts) => insts.len(),
341 _ => 0,
342 }
343 }
344}
345
346#[derive(Debug, Clone, Default)]
348pub struct MBQIStats {
349 pub num_quantifiers: usize,
351 pub total_instantiations: usize,
353 pub max_instantiations: usize,
355 pub unique_instantiations: usize,
357 pub num_checks: usize,
359 pub num_completions: usize,
361 pub num_counterexamples: usize,
363 pub num_conflicts: usize,
365 pub total_time_us: u64,
367 pub completion_time_us: u64,
369 pub cex_search_time_us: u64,
371}
372
373impl MBQIStats {
374 pub fn new() -> Self {
376 Self::default()
377 }
378
379 pub fn reset(&mut self) {
381 *self = Self::default();
382 }
383
384 pub fn avg_instantiations_per_check(&self) -> f64 {
386 if self.num_checks == 0 {
387 0.0
388 } else {
389 self.total_instantiations as f64 / self.num_checks as f64
390 }
391 }
392
393 pub fn avg_time_per_check_us(&self) -> f64 {
395 if self.num_checks == 0 {
396 0.0
397 } else {
398 self.total_time_us as f64 / self.num_checks as f64
399 }
400 }
401}
402
403impl fmt::Display for MBQIStats {
404 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
405 writeln!(f, "MBQI Statistics:")?;
406 writeln!(f, " Quantifiers: {}", self.num_quantifiers)?;
407 writeln!(f, " Total checks: {}", self.num_checks)?;
408 writeln!(f, " Total instantiations: {}", self.total_instantiations)?;
409 writeln!(f, " Unique instantiations: {}", self.unique_instantiations)?;
410 writeln!(
411 f,
412 " Avg inst/check: {:.2}",
413 self.avg_instantiations_per_check()
414 )?;
415 writeln!(f, " Counterexamples: {}", self.num_counterexamples)?;
416 writeln!(f, " Conflicts: {}", self.num_conflicts)?;
417 writeln!(
418 f,
419 " Total time: {:.2}ms",
420 self.total_time_us as f64 / 1000.0
421 )?;
422 writeln!(
423 f,
424 " Completion time: {:.2}ms",
425 self.completion_time_us as f64 / 1000.0
426 )?;
427 writeln!(
428 f,
429 " CEX search time: {:.2}ms",
430 self.cex_search_time_us as f64 / 1000.0
431 )
432 }
433}
434
435#[cfg(test)]
436mod tests {
437 use super::*;
438 use oxiz_core::interner::Key;
439
440 #[test]
441 fn test_quantified_formula_creation() {
442 let qf = QuantifiedFormula::new(TermId::new(1), SmallVec::new(), TermId::new(2), true);
443 assert!(qf.is_universal);
444 assert_eq!(qf.instantiation_count, 0);
445 assert!(qf.can_instantiate());
446 }
447
448 #[test]
449 fn test_quantified_formula_priority() {
450 let mut qf = QuantifiedFormula::with_params(
451 TermId::new(1),
452 SmallVec::new(),
453 TermId::new(2),
454 true,
455 100,
456 2.0,
457 );
458 let initial_priority = qf.priority_score();
459 qf.record_instantiation();
460 let after_priority = qf.priority_score();
461 assert!(after_priority < initial_priority);
463 }
464
465 #[test]
466 fn test_instantiation_binding_key() {
467 let mut subst = FxHashMap::default();
468 subst.insert(
469 Spur::try_from_usize(1).expect("valid spur"),
470 TermId::new(10),
471 );
472 subst.insert(
473 Spur::try_from_usize(2).expect("valid spur"),
474 TermId::new(20),
475 );
476
477 let inst = Instantiation::new(TermId::new(1), subst, TermId::new(3), 0);
478 let key = inst.binding_key();
479 assert_eq!(key.len(), 2);
480 assert!(key[0].0 <= key[1].0);
482 }
483
484 #[test]
485 fn test_mbqi_result_predicates() {
486 let sat = MBQIResult::Satisfied;
487 assert!(sat.is_sat());
488 assert!(!sat.is_unsat());
489 assert!(!sat.has_instantiations());
490
491 let conflict = MBQIResult::Conflict {
492 quantifier: TermId::new(1),
493 reason: vec![],
494 };
495 assert!(!conflict.is_sat());
496 assert!(conflict.is_unsat());
497
498 let inst = MBQIResult::NewInstantiations(vec![]);
499 assert!(inst.has_instantiations());
500 assert_eq!(inst.num_instantiations(), 0);
501 }
502
503 #[test]
504 fn test_stats_initialization() {
505 let stats = MBQIStats::new();
506 assert_eq!(stats.num_quantifiers, 0);
507 assert_eq!(stats.total_instantiations, 0);
508 assert_eq!(stats.avg_instantiations_per_check(), 0.0);
509 }
510
511 #[test]
512 fn test_stats_averages() {
513 let mut stats = MBQIStats::new();
514 stats.num_checks = 10;
515 stats.total_instantiations = 50;
516 stats.total_time_us = 1000;
517
518 assert_eq!(stats.avg_instantiations_per_check(), 5.0);
519 assert_eq!(stats.avg_time_per_check_us(), 100.0);
520 }
521
522 #[test]
523 fn test_instantiation_reason_display() {
524 assert_eq!(
525 format!("{}", InstantiationReason::ModelBased),
526 "model-based"
527 );
528 assert_eq!(format!("{}", InstantiationReason::EMatching), "e-matching");
529 assert_eq!(format!("{}", InstantiationReason::Conflict), "conflict");
530 }
531}