1#![allow(missing_docs)]
15#![allow(dead_code)]
16
17use oxiz_core::ast::{TermId, TermManager};
18use oxiz_core::sort::SortId;
19use rustc_hash::{FxHashMap, FxHashSet};
20use std::fmt;
21
22use super::QuantifiedFormula;
23use super::model_completion::CompletedModel;
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
27pub struct UniverseSize(pub usize);
28
29impl UniverseSize {
30 pub fn new(size: usize) -> Self {
32 Self(size)
33 }
34
35 pub fn size(&self) -> usize {
37 self.0
38 }
39
40 pub fn is_trivial(&self) -> bool {
42 self.0 <= 1
43 }
44
45 pub fn next(&self) -> Self {
47 Self(self.0 + 1)
48 }
49
50 pub fn total_assignments(&self, num_vars: usize) -> usize {
52 self.0.pow(num_vars as u32)
53 }
54}
55
56impl fmt::Display for UniverseSize {
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 write!(f, "|U|={}", self.0)
59 }
60}
61
62#[derive(Debug, Clone)]
64pub struct FiniteModel {
65 pub model: CompletedModel,
67 pub universe_sizes: FxHashMap<SortId, UniverseSize>,
69 pub total_elements: usize,
71}
72
73impl FiniteModel {
74 pub fn new(model: CompletedModel) -> Self {
76 let mut total_elements = 0;
77 let mut universe_sizes = FxHashMap::default();
78
79 for (sort, universe) in &model.universes {
80 let size = UniverseSize::new(universe.len());
81 universe_sizes.insert(*sort, size);
82 total_elements += universe.len();
83 }
84
85 Self {
86 model,
87 universe_sizes,
88 total_elements,
89 }
90 }
91
92 pub fn universe_size(&self, sort: SortId) -> Option<UniverseSize> {
94 self.universe_sizes.get(&sort).copied()
95 }
96
97 pub fn satisfies(
99 &self,
100 _quantifier: &QuantifiedFormula,
101 _manager: &TermManager,
102 ) -> Option<bool> {
103 None
105 }
106}
107
108#[derive(Debug)]
110pub struct FiniteModelFinder {
111 min_size: UniverseSize,
113 max_size: UniverseSize,
115 current_bound: UniverseSize,
117 symmetry_breaker: SymmetryBreaker,
119 stats: FinderStats,
121 learned_constraints: Vec<TermId>,
123}
124
125impl FiniteModelFinder {
126 pub fn new() -> Self {
128 Self {
129 min_size: UniverseSize::new(1),
130 max_size: UniverseSize::new(8),
131 current_bound: UniverseSize::new(1),
132 symmetry_breaker: SymmetryBreaker::new(),
133 stats: FinderStats::default(),
134 learned_constraints: Vec::new(),
135 }
136 }
137
138 pub fn with_bounds(min: usize, max: usize) -> Self {
140 let mut finder = Self::new();
141 finder.min_size = UniverseSize::new(min);
142 finder.max_size = UniverseSize::new(max);
143 finder.current_bound = UniverseSize::new(min);
144 finder
145 }
146
147 pub fn find_model(
149 &mut self,
150 quantifiers: &[QuantifiedFormula],
151 partial_model: &FxHashMap<TermId, TermId>,
152 manager: &mut TermManager,
153 ) -> Result<FiniteModel, FinderError> {
154 self.stats.num_searches += 1;
155 self.current_bound = self.min_size;
156
157 while self.current_bound <= self.max_size {
159 self.stats.num_iterations += 1;
160
161 match self.find_model_with_bound(
162 quantifiers,
163 partial_model,
164 manager,
165 self.current_bound,
166 ) {
167 Ok(model) => {
168 self.stats.num_models_found += 1;
169 return Ok(model);
170 }
171 Err(FinderError::UnsatAtBound) => {
172 self.current_bound = self.current_bound.next();
174 }
175 Err(e) => return Err(e),
176 }
177 }
178
179 Err(FinderError::ExceededMaxBound)
180 }
181
182 fn find_model_with_bound(
184 &mut self,
185 quantifiers: &[QuantifiedFormula],
186 partial_model: &FxHashMap<TermId, TermId>,
187 manager: &mut TermManager,
188 bound: UniverseSize,
189 ) -> Result<FiniteModel, FinderError> {
190 let mut model = CompletedModel::new();
192 model.assignments = partial_model.clone();
193
194 let uninterp_sorts = self.identify_uninterpreted_sorts(quantifiers, manager);
196
197 for sort in uninterp_sorts {
199 let universe = self.create_universe(sort, bound, manager);
200 model.universes.insert(sort, universe);
201 }
202
203 let sb_constraints =
205 self.symmetry_breaker
206 .generate_constraints(&model, quantifiers, manager);
207 self.learned_constraints.extend(sb_constraints);
208
209 let finite_model = FiniteModel::new(model);
211
212 for quantifier in quantifiers {
214 if let Some(false) = finite_model.satisfies(quantifier, manager) {
215 return Err(FinderError::UnsatAtBound);
216 }
217 }
218
219 Ok(finite_model)
220 }
221
222 fn identify_uninterpreted_sorts(
224 &self,
225 quantifiers: &[QuantifiedFormula],
226 manager: &TermManager,
227 ) -> Vec<SortId> {
228 let mut sorts = Vec::new();
229 let mut seen = FxHashSet::default();
230
231 for quantifier in quantifiers {
232 for &(_name, sort) in &quantifier.bound_vars {
233 if self.is_uninterpreted(sort, manager) && seen.insert(sort) {
234 sorts.push(sort);
235 }
236 }
237 }
238
239 sorts
240 }
241
242 fn is_uninterpreted(&self, sort: SortId, manager: &TermManager) -> bool {
243 sort != manager.sorts.bool_sort
244 && sort != manager.sorts.int_sort
245 && sort != manager.sorts.real_sort
246 }
247
248 fn create_universe(
250 &self,
251 sort: SortId,
252 size: UniverseSize,
253 manager: &mut TermManager,
254 ) -> Vec<TermId> {
255 let mut universe = Vec::new();
256
257 for i in 0..size.size() {
258 let name = format!("u!{}!{}", sort.0, i);
259 let elem = manager.mk_var(&name, sort);
260 universe.push(elem);
261 }
262
263 universe
264 }
265
266 pub fn stats(&self) -> &FinderStats {
268 &self.stats
269 }
270
271 pub fn reset(&mut self) {
273 self.current_bound = self.min_size;
274 self.learned_constraints.clear();
275 }
276}
277
278impl Default for FiniteModelFinder {
279 fn default() -> Self {
280 Self::new()
281 }
282}
283
284#[derive(Debug)]
286pub struct SymmetryBreaker {
287 strategy: SymmetryStrategy,
289 stats: SymmetryStats,
291}
292
293impl SymmetryBreaker {
294 pub fn new() -> Self {
296 Self {
297 strategy: SymmetryStrategy::LeastNumber,
298 stats: SymmetryStats::default(),
299 }
300 }
301
302 pub fn with_strategy(strategy: SymmetryStrategy) -> Self {
304 let mut breaker = Self::new();
305 breaker.strategy = strategy;
306 breaker
307 }
308
309 pub fn generate_constraints(
311 &mut self,
312 model: &CompletedModel,
313 quantifiers: &[QuantifiedFormula],
314 manager: &mut TermManager,
315 ) -> Vec<TermId> {
316 let mut constraints = Vec::new();
317
318 match self.strategy {
319 SymmetryStrategy::None => {}
320 SymmetryStrategy::LeastNumber => {
321 constraints.extend(self.generate_least_number_constraints(model, manager));
322 }
323 SymmetryStrategy::OrderConstraints => {
324 constraints.extend(self.generate_order_constraints(model, quantifiers, manager));
325 }
326 SymmetryStrategy::PredecessorConstraints => {
327 constraints.extend(self.generate_predecessor_constraints(model, manager));
328 }
329 }
330
331 self.stats.num_constraints_generated += constraints.len();
332 constraints
333 }
334
335 fn generate_least_number_constraints(
338 &self,
339 model: &CompletedModel,
340 manager: &mut TermManager,
341 ) -> Vec<TermId> {
342 let mut constraints = Vec::new();
343
344 for universe in model.universes.values() {
345 for i in 0..universe.len() {
348 for j in (i + 1)..universe.len() {
349 let elem_i = universe[i];
352 let elem_j = universe[j];
353
354 let cond = manager.mk_eq(elem_j, elem_j); let conseq = manager.mk_eq(elem_i, elem_i); let constraint = manager.mk_implies(cond, conseq);
359 constraints.push(constraint);
360 }
361 }
362 }
363
364 constraints
365 }
366
367 fn generate_order_constraints(
369 &self,
370 model: &CompletedModel,
371 _quantifiers: &[QuantifiedFormula],
372 manager: &mut TermManager,
373 ) -> Vec<TermId> {
374 let mut constraints = Vec::new();
375
376 for universe in model.universes.values() {
377 for i in 0..(universe.len().saturating_sub(1)) {
379 let elem_i = universe[i];
380 let elem_j = universe[i + 1];
381
382 let constraint = manager.mk_lt(elem_i, elem_j);
386 constraints.push(constraint);
387 }
388 }
389
390 constraints
391 }
392
393 fn generate_predecessor_constraints(
395 &self,
396 model: &CompletedModel,
397 manager: &mut TermManager,
398 ) -> Vec<TermId> {
399 let mut constraints = Vec::new();
400
401 for universe in model.universes.values() {
402 if universe.len() < 2 {
403 continue;
404 }
405
406 for i in 1..universe.len() {
408 let elem = universe[i];
409 let mut predecessors = Vec::new();
410
411 #[allow(clippy::needless_range_loop)]
412 for j in 0..i {
413 predecessors.push(universe[j]);
414 }
415
416 if !predecessors.is_empty() {
418 let mut disj_terms = Vec::new();
419 for pred in predecessors {
420 let rel = manager.mk_eq(pred, elem);
422 disj_terms.push(rel);
423 }
424 let constraint = manager.mk_or(disj_terms);
425 constraints.push(constraint);
426 }
427 }
428 }
429
430 constraints
431 }
432
433 pub fn stats(&self) -> &SymmetryStats {
435 &self.stats
436 }
437}
438
439impl Default for SymmetryBreaker {
440 fn default() -> Self {
441 Self::new()
442 }
443}
444
445#[derive(Debug, Clone, Copy, PartialEq, Eq)]
447pub enum SymmetryStrategy {
448 None,
450 LeastNumber,
452 OrderConstraints,
454 PredecessorConstraints,
456}
457
458#[derive(Debug)]
460pub struct DomainEnumerator {
461 max_assignments: usize,
463 current_index: usize,
465}
466
467impl DomainEnumerator {
468 pub fn new(max_assignments: usize) -> Self {
470 Self {
471 max_assignments,
472 current_index: 0,
473 }
474 }
475
476 pub fn enumerate(&mut self, domains: &[Vec<TermId>]) -> Vec<Vec<TermId>> {
478 let mut results = Vec::new();
479 let mut indices = vec![0usize; domains.len()];
480
481 while results.len() < self.max_assignments {
482 let assignment: Vec<TermId> = indices
484 .iter()
485 .enumerate()
486 .filter_map(|(i, &idx)| domains.get(i).and_then(|d| d.get(idx).copied()))
487 .collect();
488
489 if assignment.len() == domains.len() {
490 results.push(assignment);
491 }
492
493 let mut carry = true;
495 for (i, idx) in indices.iter_mut().enumerate() {
496 if carry {
497 *idx += 1;
498 let limit = domains.get(i).map_or(1, |d| d.len());
499 if *idx >= limit {
500 *idx = 0;
501 } else {
502 carry = false;
503 }
504 }
505 }
506
507 if carry {
508 break; }
510 }
511
512 results
513 }
514
515 pub fn reset(&mut self) {
517 self.current_index = 0;
518 }
519}
520
521#[derive(Debug)]
523pub struct BoundedModelChecker {
524 depth_bound: usize,
526 max_depth: usize,
528}
529
530impl BoundedModelChecker {
531 pub fn new() -> Self {
533 Self {
534 depth_bound: 0,
535 max_depth: 10,
536 }
537 }
538
539 pub fn check(
541 &mut self,
542 _quantifiers: &[QuantifiedFormula],
543 _model: &CompletedModel,
544 _manager: &TermManager,
545 ) -> CheckResult {
546 CheckResult::Unknown
548 }
549
550 pub fn increase_bound(&mut self) {
552 self.depth_bound += 1;
553 }
554
555 pub fn at_max_depth(&self) -> bool {
557 self.depth_bound >= self.max_depth
558 }
559}
560
561impl Default for BoundedModelChecker {
562 fn default() -> Self {
563 Self::new()
564 }
565}
566
567#[derive(Debug, Clone, Copy, PartialEq, Eq)]
569pub enum CheckResult {
570 Sat,
572 Unsat,
574 Unknown,
576}
577
578#[derive(Debug, Clone)]
580pub enum FinderError {
581 UnsatAtBound,
583 ExceededMaxBound,
585 ResourceLimit,
587 InvalidConfig(String),
589}
590
591impl fmt::Display for FinderError {
592 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
593 match self {
594 Self::UnsatAtBound => write!(f, "Unsatisfiable at current bound"),
595 Self::ExceededMaxBound => write!(f, "Exceeded maximum universe size"),
596 Self::ResourceLimit => write!(f, "Resource limit exceeded"),
597 Self::InvalidConfig(msg) => write!(f, "Invalid configuration: {}", msg),
598 }
599 }
600}
601
602impl std::error::Error for FinderError {}
603
604#[derive(Debug, Clone, Default)]
606pub struct FinderStats {
607 pub num_searches: usize,
609 pub num_iterations: usize,
611 pub num_models_found: usize,
613}
614
615#[derive(Debug, Clone, Default)]
617pub struct SymmetryStats {
618 pub num_constraints_generated: usize,
620}
621
622#[cfg(test)]
623mod tests {
624 use super::*;
625
626 #[test]
627 fn test_universe_size_creation() {
628 let size = UniverseSize::new(5);
629 assert_eq!(size.size(), 5);
630 }
631
632 #[test]
633 fn test_universe_size_trivial() {
634 assert!(UniverseSize::new(0).is_trivial());
635 assert!(UniverseSize::new(1).is_trivial());
636 assert!(!UniverseSize::new(2).is_trivial());
637 }
638
639 #[test]
640 fn test_universe_size_next() {
641 let size = UniverseSize::new(3);
642 assert_eq!(size.next().size(), 4);
643 }
644
645 #[test]
646 fn test_universe_size_assignments() {
647 let size = UniverseSize::new(2);
648 assert_eq!(size.total_assignments(3), 8); }
650
651 #[test]
652 fn test_universe_size_display() {
653 let size = UniverseSize::new(5);
654 assert_eq!(format!("{}", size), "|U|=5");
655 }
656
657 #[test]
658 fn test_finite_model_creation() {
659 let model = CompletedModel::new();
660 let finite = FiniteModel::new(model);
661 assert_eq!(finite.total_elements, 0);
662 }
663
664 #[test]
665 fn test_finite_model_finder_creation() {
666 let finder = FiniteModelFinder::new();
667 assert_eq!(finder.min_size.size(), 1);
668 assert_eq!(finder.max_size.size(), 8);
669 }
670
671 #[test]
672 fn test_finite_model_finder_with_bounds() {
673 let finder = FiniteModelFinder::with_bounds(2, 10);
674 assert_eq!(finder.min_size.size(), 2);
675 assert_eq!(finder.max_size.size(), 10);
676 }
677
678 #[test]
679 fn test_symmetry_breaker_creation() {
680 let breaker = SymmetryBreaker::new();
681 assert_eq!(breaker.strategy, SymmetryStrategy::LeastNumber);
682 }
683
684 #[test]
685 fn test_symmetry_breaker_with_strategy() {
686 let breaker = SymmetryBreaker::with_strategy(SymmetryStrategy::OrderConstraints);
687 assert_eq!(breaker.strategy, SymmetryStrategy::OrderConstraints);
688 }
689
690 #[test]
691 fn test_symmetry_strategy_equality() {
692 assert_eq!(SymmetryStrategy::None, SymmetryStrategy::None);
693 assert_ne!(SymmetryStrategy::None, SymmetryStrategy::LeastNumber);
694 }
695
696 #[test]
697 fn test_domain_enumerator_creation() {
698 let enumerator = DomainEnumerator::new(100);
699 assert_eq!(enumerator.max_assignments, 100);
700 }
701
702 #[test]
703 fn test_domain_enumerator_enumerate_empty() {
704 let mut enumerator = DomainEnumerator::new(100);
705 let results = enumerator.enumerate(&[]);
706 assert_eq!(results.len(), 1);
707 assert!(results[0].is_empty());
708 }
709
710 #[test]
711 fn test_domain_enumerator_enumerate_single() {
712 let mut enumerator = DomainEnumerator::new(100);
713 let domains = vec![vec![TermId::new(1), TermId::new(2)]];
714 let results = enumerator.enumerate(&domains);
715 assert_eq!(results.len(), 2);
716 }
717
718 #[test]
719 fn test_bounded_model_checker_creation() {
720 let checker = BoundedModelChecker::new();
721 assert_eq!(checker.depth_bound, 0);
722 assert_eq!(checker.max_depth, 10);
723 }
724
725 #[test]
726 fn test_bounded_model_checker_depth() {
727 let mut checker = BoundedModelChecker::new();
728 assert!(!checker.at_max_depth());
729 for _ in 0..10 {
730 checker.increase_bound();
731 }
732 assert!(checker.at_max_depth());
733 }
734
735 #[test]
736 fn test_check_result_equality() {
737 assert_eq!(CheckResult::Sat, CheckResult::Sat);
738 assert_ne!(CheckResult::Sat, CheckResult::Unsat);
739 }
740
741 #[test]
742 fn test_finder_error_display() {
743 let err = FinderError::UnsatAtBound;
744 assert!(format!("{}", err).contains("Unsatisfiable"));
745 }
746}