1#![allow(dead_code, missing_docs)] #[allow(unused_imports)]
13use crate::prelude::*;
14
15#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct ModelValue {
18 pub term: TermId,
20 pub value: Value,
22 pub theory: Theory,
24 pub is_witness: bool,
26}
27
28#[derive(Debug, Clone, PartialEq, Eq)]
30pub enum Value {
31 Bool(bool),
33 Int(i64),
35 Rat(i64, u64),
37 BitVec(u64, usize),
39 Array(Box<ArrayValue>),
41 UFApp(String, Vec<Value>),
43 Constructor(String, Vec<Value>),
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
49pub struct ArrayValue {
50 pub default: Value,
52 pub mappings: BTreeMap<Value, Value>,
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
58pub enum Theory {
59 Core,
60 Arithmetic,
61 BitVector,
62 Array,
63 Datatype,
64 String,
65 Uninterpreted,
66}
67
68pub type TermId = usize;
70
71#[derive(Debug, Clone, Default)]
73pub struct ModelBuilderStats {
74 pub models_built: u64,
75 pub witnesses_generated: u64,
76 pub consistency_checks: u64,
77 pub minimization_steps: u64,
78 pub theory_calls: u64,
79}
80
81#[derive(Debug, Clone)]
83pub struct ModelBuilderConfig {
84 pub enable_minimization: bool,
86 pub generate_witnesses: bool,
88 pub check_consistency: bool,
90 pub max_minimization_iters: usize,
92}
93
94impl Default for ModelBuilderConfig {
95 fn default() -> Self {
96 Self {
97 enable_minimization: true,
98 generate_witnesses: true,
99 check_consistency: true,
100 max_minimization_iters: 10,
101 }
102 }
103}
104
105pub struct AdvancedModelBuilder {
107 assignments: FxHashMap<TermId, ModelValue>,
109 equiv_classes: FxHashMap<TermId, TermId>,
111 witness_obligations: FxHashSet<TermId>,
113 theory_models: FxHashMap<Theory, TheoryModel>,
115 config: ModelBuilderConfig,
117 stats: ModelBuilderStats,
119}
120
121#[derive(Debug, Clone)]
123struct TheoryModel {
124 assignments: FxHashMap<TermId, Value>,
126 constraints: Vec<TermId>,
128}
129
130impl AdvancedModelBuilder {
131 pub fn new(config: ModelBuilderConfig) -> Self {
133 Self {
134 assignments: FxHashMap::default(),
135 equiv_classes: FxHashMap::default(),
136 witness_obligations: FxHashSet::default(),
137 theory_models: FxHashMap::default(),
138 config,
139 stats: ModelBuilderStats::default(),
140 }
141 }
142
143 pub fn build_model(
145 &mut self,
146 boolean_assignments: &FxHashMap<TermId, bool>,
147 equiv_classes: &FxHashMap<TermId, TermId>,
148 ) -> Result<Model, String> {
149 self.stats.models_built += 1;
150 self.equiv_classes = equiv_classes.clone();
151
152 self.build_theory_models(boolean_assignments)?;
154
155 if self.config.generate_witnesses {
157 self.generate_witnesses()?;
158 }
159
160 if self.config.check_consistency {
162 self.check_cross_theory_consistency()?;
163 }
164
165 if self.config.enable_minimization {
167 self.minimize_model()?;
168 }
169
170 let model = Model {
172 assignments: self.assignments.clone(),
173 equiv_classes: self.equiv_classes.clone(),
174 };
175
176 Ok(model)
177 }
178
179 fn build_theory_models(
181 &mut self,
182 boolean_assignments: &FxHashMap<TermId, bool>,
183 ) -> Result<(), String> {
184 self.build_arithmetic_model(boolean_assignments)?;
186
187 self.build_bitvector_model(boolean_assignments)?;
189
190 self.build_array_model(boolean_assignments)?;
192
193 self.build_datatype_model(boolean_assignments)?;
195
196 self.build_uf_model(boolean_assignments)?;
198
199 Ok(())
200 }
201
202 fn build_arithmetic_model(
204 &mut self,
205 boolean_assignments: &FxHashMap<TermId, bool>,
206 ) -> Result<(), String> {
207 self.stats.theory_calls += 1;
208
209 let mut bounds: FxHashMap<TermId, (Option<i64>, Option<i64>)> = FxHashMap::default();
211
212 for (&term, &value) in boolean_assignments {
213 if value {
214 self.update_arithmetic_bounds(term, &mut bounds)?;
216 }
217 }
218
219 for (term, (lower, upper)) in bounds {
221 let value = match (lower, upper) {
222 (Some(l), Some(u)) if l <= u => {
223 Value::Int((l + u) / 2)
225 }
226 (Some(l), None) => Value::Int(l),
227 (None, Some(u)) => Value::Int(u),
228 (None, None) => Value::Int(0),
229 _ => return Err("Inconsistent arithmetic bounds".to_string()),
230 };
231
232 self.assignments.insert(
233 term,
234 ModelValue {
235 term,
236 value,
237 theory: Theory::Arithmetic,
238 is_witness: false,
239 },
240 );
241 }
242
243 Ok(())
244 }
245
246 fn update_arithmetic_bounds(
248 &self,
249 _term: TermId,
250 bounds: &mut FxHashMap<TermId, (Option<i64>, Option<i64>)>,
251 ) -> Result<(), String> {
252 let var = 0; let bound_pair = bounds.entry(var).or_insert((None, None));
256
257 if bound_pair.0.is_none_or(|v| v < 0) {
259 bound_pair.0 = Some(0);
260 }
261
262 Ok(())
263 }
264
265 fn build_bitvector_model(
267 &mut self,
268 _boolean_assignments: &FxHashMap<TermId, bool>,
269 ) -> Result<(), String> {
270 self.stats.theory_calls += 1;
271
272 Ok(())
276 }
277
278 fn build_array_model(
280 &mut self,
281 _boolean_assignments: &FxHashMap<TermId, bool>,
282 ) -> Result<(), String> {
283 self.stats.theory_calls += 1;
284
285 Ok(())
289 }
290
291 fn build_datatype_model(
293 &mut self,
294 _boolean_assignments: &FxHashMap<TermId, bool>,
295 ) -> Result<(), String> {
296 self.stats.theory_calls += 1;
297
298 Ok(())
301 }
302
303 fn build_uf_model(
305 &mut self,
306 _boolean_assignments: &FxHashMap<TermId, bool>,
307 ) -> Result<(), String> {
308 self.stats.theory_calls += 1;
309
310 Ok(())
314 }
315
316 fn generate_witnesses(&mut self) -> Result<(), String> {
318 let obligations: Vec<_> = self.witness_obligations.iter().copied().collect();
319
320 for term in obligations {
321 self.stats.witnesses_generated += 1;
322
323 let theory = self.determine_theory(term);
325 let witness_value = self.generate_theory_witness(term, theory)?;
326
327 self.assignments.insert(
328 term,
329 ModelValue {
330 term,
331 value: witness_value,
332 theory,
333 is_witness: true,
334 },
335 );
336 }
337
338 Ok(())
339 }
340
341 fn determine_theory(&self, _term: TermId) -> Theory {
343 Theory::Core
345 }
346
347 fn generate_theory_witness(&self, _term: TermId, theory: Theory) -> Result<Value, String> {
349 match theory {
350 Theory::Arithmetic => Ok(Value::Int(0)),
351 Theory::BitVector => Ok(Value::BitVec(0, 32)),
352 Theory::Core => Ok(Value::Bool(false)),
353 _ => Ok(Value::Int(0)),
354 }
355 }
356
357 fn check_cross_theory_consistency(&mut self) -> Result<(), String> {
359 self.stats.consistency_checks += 1;
360
361 let mut shared_terms: FxHashMap<TermId, Vec<Theory>> = FxHashMap::default();
363
364 for assignment in self.assignments.values() {
365 shared_terms
366 .entry(assignment.term)
367 .or_default()
368 .push(assignment.theory);
369 }
370
371 for (term, theories) in shared_terms {
372 if theories.len() > 1 {
373 let values: Vec<_> = theories
375 .iter()
376 .filter_map(|&theory| {
377 self.theory_models
378 .get(&theory)
379 .and_then(|m| m.assignments.get(&term))
380 })
381 .collect();
382
383 if values.windows(2).any(|w| w[0] != w[1]) {
384 return Err(format!("Cross-theory inconsistency for term {}", term));
385 }
386 }
387 }
388
389 Ok(())
390 }
391
392 fn minimize_model(&mut self) -> Result<(), String> {
394 let mut iteration = 0;
395
396 while iteration < self.config.max_minimization_iters {
397 self.stats.minimization_steps += 1;
398 iteration += 1;
399
400 let initial_size = self.assignments.len();
401
402 let terms: Vec<_> = self.assignments.keys().copied().collect();
404
405 for term in terms {
406 if !self.is_assignment_necessary(term)? {
408 self.assignments.remove(&term);
409 }
410 }
411
412 if self.assignments.len() == initial_size {
414 break;
415 }
416 }
417
418 Ok(())
419 }
420
421 fn is_assignment_necessary(&self, _term: TermId) -> Result<bool, String> {
423 Ok(true)
426 }
427
428 pub fn add_witness_obligation(&mut self, term: TermId) {
430 self.witness_obligations.insert(term);
431 }
432
433 pub fn stats(&self) -> &ModelBuilderStats {
435 &self.stats
436 }
437
438 pub fn reset(&mut self) {
440 self.assignments.clear();
441 self.equiv_classes.clear();
442 self.witness_obligations.clear();
443 self.theory_models.clear();
444 }
445}
446
447#[derive(Debug, Clone)]
449pub struct Model {
450 pub assignments: FxHashMap<TermId, ModelValue>,
452 pub equiv_classes: FxHashMap<TermId, TermId>,
454}
455
456impl Model {
457 pub fn eval(&self, term: TermId) -> Option<&Value> {
459 if let Some(assignment) = self.assignments.get(&term) {
461 return Some(&assignment.value);
462 }
463
464 if let Some(&repr) = self.equiv_classes.get(&term)
466 && let Some(assignment) = self.assignments.get(&repr)
467 {
468 return Some(&assignment.value);
469 }
470
471 None
472 }
473
474 pub fn is_true(&self, term: TermId) -> bool {
476 matches!(self.eval(term), Some(Value::Bool(true)))
477 }
478
479 pub fn theory_assignments(&self, theory: Theory) -> Vec<&ModelValue> {
481 self.assignments
482 .values()
483 .filter(|v| v.theory == theory)
484 .collect()
485 }
486
487 pub fn witnesses(&self) -> Vec<&ModelValue> {
489 self.assignments.values().filter(|v| v.is_witness).collect()
490 }
491}
492
493#[cfg(test)]
494mod tests {
495 use super::*;
496
497 #[test]
498 fn test_model_builder_creation() {
499 let config = ModelBuilderConfig::default();
500 let builder = AdvancedModelBuilder::new(config);
501 assert_eq!(builder.stats.models_built, 0);
502 }
503
504 #[test]
505 fn test_arithmetic_model_simple() {
506 let config = ModelBuilderConfig::default();
507 let mut builder = AdvancedModelBuilder::new(config);
508
509 let assignments = FxHashMap::default();
510 let equiv_classes = FxHashMap::default();
511
512 let result = builder.build_model(&assignments, &equiv_classes);
513 assert!(result.is_ok());
514 assert_eq!(builder.stats.models_built, 1);
515 }
516
517 #[test]
518 fn test_witness_generation() {
519 let config = ModelBuilderConfig {
520 generate_witnesses: true,
521 ..Default::default()
522 };
523 let mut builder = AdvancedModelBuilder::new(config);
524
525 builder.add_witness_obligation(42);
526 assert!(builder.witness_obligations.contains(&42));
527
528 let result = builder.generate_witnesses();
529 assert!(result.is_ok());
530 assert_eq!(builder.stats.witnesses_generated, 1);
531 }
532
533 #[test]
534 fn test_model_evaluation() {
535 let mut assignments = FxHashMap::default();
536 assignments.insert(
537 1,
538 ModelValue {
539 term: 1,
540 value: Value::Int(42),
541 theory: Theory::Arithmetic,
542 is_witness: false,
543 },
544 );
545
546 let model = Model {
547 assignments,
548 equiv_classes: FxHashMap::default(),
549 };
550
551 assert_eq!(model.eval(1), Some(&Value::Int(42)));
552 assert_eq!(model.eval(2), None);
553 }
554
555 #[test]
556 fn test_model_with_equivalence() {
557 let mut assignments = FxHashMap::default();
558 assignments.insert(
559 1,
560 ModelValue {
561 term: 1,
562 value: Value::Int(42),
563 theory: Theory::Arithmetic,
564 is_witness: false,
565 },
566 );
567
568 let mut equiv_classes = FxHashMap::default();
569 equiv_classes.insert(2, 1); let model = Model {
572 assignments,
573 equiv_classes,
574 };
575
576 assert_eq!(model.eval(2), Some(&Value::Int(42)));
577 }
578
579 #[test]
580 fn test_theory_assignments_filter() {
581 let mut assignments = FxHashMap::default();
582 assignments.insert(
583 1,
584 ModelValue {
585 term: 1,
586 value: Value::Int(42),
587 theory: Theory::Arithmetic,
588 is_witness: false,
589 },
590 );
591 assignments.insert(
592 2,
593 ModelValue {
594 term: 2,
595 value: Value::BitVec(0xff, 8),
596 theory: Theory::BitVector,
597 is_witness: false,
598 },
599 );
600
601 let model = Model {
602 assignments,
603 equiv_classes: FxHashMap::default(),
604 };
605
606 let arith_assignments = model.theory_assignments(Theory::Arithmetic);
607 assert_eq!(arith_assignments.len(), 1);
608 assert_eq!(arith_assignments[0].term, 1);
609 }
610
611 #[test]
612 fn test_witness_filter() {
613 let mut assignments = FxHashMap::default();
614 assignments.insert(
615 1,
616 ModelValue {
617 term: 1,
618 value: Value::Int(0),
619 theory: Theory::Arithmetic,
620 is_witness: true,
621 },
622 );
623 assignments.insert(
624 2,
625 ModelValue {
626 term: 2,
627 value: Value::Int(5),
628 theory: Theory::Arithmetic,
629 is_witness: false,
630 },
631 );
632
633 let model = Model {
634 assignments,
635 equiv_classes: FxHashMap::default(),
636 };
637
638 let witnesses = model.witnesses();
639 assert_eq!(witnesses.len(), 1);
640 assert_eq!(witnesses[0].term, 1);
641 }
642
643 #[test]
644 fn test_consistency_check() {
645 let config = ModelBuilderConfig {
646 check_consistency: true,
647 ..Default::default()
648 };
649 let mut builder = AdvancedModelBuilder::new(config);
650
651 let result = builder.check_cross_theory_consistency();
652 assert!(result.is_ok());
653 assert_eq!(builder.stats.consistency_checks, 1);
654 }
655
656 #[test]
657 fn test_minimization() {
658 let config = ModelBuilderConfig {
659 enable_minimization: true,
660 max_minimization_iters: 5,
661 ..Default::default()
662 };
663 let mut builder = AdvancedModelBuilder::new(config);
664
665 builder.assignments.insert(
667 1,
668 ModelValue {
669 term: 1,
670 value: Value::Int(42),
671 theory: Theory::Arithmetic,
672 is_witness: false,
673 },
674 );
675
676 let result = builder.minimize_model();
677 assert!(result.is_ok());
678 assert!(builder.stats.minimization_steps > 0);
679 }
680
681 #[test]
682 fn test_reset() {
683 let mut builder = AdvancedModelBuilder::new(ModelBuilderConfig::default());
684
685 builder.assignments.insert(
686 1,
687 ModelValue {
688 term: 1,
689 value: Value::Int(42),
690 theory: Theory::Arithmetic,
691 is_witness: false,
692 },
693 );
694 builder.add_witness_obligation(2);
695
696 builder.reset();
697 assert!(builder.assignments.is_empty());
698 assert!(builder.witness_obligations.is_empty());
699 }
700}