1use crate::{
2 btreeset,
3 dot_writer::{Dot, DotWriter},
4 literal::{Literal, LiteralManager, Polarity, Variable, VariableIdx},
5 manager::{
6 dimacs,
7 model::Models,
8 options::{GarbageCollection, SddOptions},
9 },
10 sdd::{Decision, Element, Sdd, SddId, SddRef, SddType},
11 util::set_bits_indices,
12 vtree::{
13 rotate_partition_left, rotate_partition_right, split_nodes_for_left_rotate,
14 split_nodes_for_right_rotate, split_nodes_for_swap, swap_partition, Direction, Fragment,
15 LeftRotateSplit, RightRotateSplit, VTreeIdx, VTreeManager, VTreeOrder, VTreeRef,
16 },
17};
18use bitvec::prelude::*;
19use rustc_hash::{FxHashMap, FxHashSet};
20use std::{cell::RefCell, cmp::Ordering, collections::BTreeSet, ops::BitOr};
21
22use anyhow::{anyhow, bail, Context, Result};
23use tracing::instrument;
24
25use super::options::{FragmentHeuristic, MinimizationCutoff};
26
27#[derive(Clone, Eq, PartialEq, Hash, Debug, Copy)]
29pub(crate) enum Operation {
30 Conjoin,
31 Disjoin,
32}
33
34impl Operation {
35 fn zero(self) -> SddId {
37 match self {
38 Operation::Conjoin => FALSE_SDD_IDX,
39 Operation::Disjoin => TRUE_SDD_IDX,
40 }
41 }
42}
43
44pub(crate) enum CachedOperation {
46 BinOp(SddId, Operation, SddId),
47 Neg(SddId),
48}
49
50#[derive(Eq, PartialEq, Hash, Debug, Clone)]
51struct Entry {
52 fst: SddId,
53 snd: SddId,
54 op: Operation,
55}
56
57impl Entry {
59 fn contains_id(&self, id: SddId) -> bool {
60 self.fst == id || self.snd == id
61 }
62}
63
64#[derive(Debug, Clone)]
66pub struct GCStatistics {
67 pub nodes_collected: usize,
68 pub gc_triggered: usize,
69}
70
71impl GCStatistics {
72 fn collected(&mut self, nodes_collected: usize) {
73 self.gc_triggered += 1;
74 self.nodes_collected += nodes_collected;
75 }
76}
77
78#[allow(clippy::module_name_repetitions)]
97#[derive(Debug)]
98pub struct SddManager {
99 options: SddOptions,
100
101 vtree_manager: RefCell<VTreeManager>,
102 literal_manager: RefCell<LiteralManager>,
103
104 unique_table: RefCell<FxHashMap<SddId, SddRef>>,
107
108 op_cache: RefCell<FxHashMap<Entry, SddId>>,
110 neg_cache: RefCell<FxHashMap<SddId, SddId>>,
111
112 next_idx: RefCell<SddId>,
113
114 rotating: RefCell<bool>,
117
118 gc_stats: RefCell<GCStatistics>,
119
120 apply_level: RefCell<u32>,
121}
122
123pub(crate) const FALSE_SDD_IDX: SddId = SddId(0);
125pub(crate) const TRUE_SDD_IDX: SddId = SddId(1);
126
127impl SddManager {
128 #[must_use]
137 pub fn new(options: &SddOptions) -> SddManager {
138 assert!(
139 !options.variables.is_empty(),
140 "SddManager must be initialized with at least one variable!"
141 );
142
143 let mut unique_table = RefCell::new(FxHashMap::default());
144 let ff = SddRef::new(Sdd::new_false());
145 let tt = SddRef::new(Sdd::new_true());
146
147 assert_eq!(tt.id(), TRUE_SDD_IDX);
149 assert_eq!(ff.id(), FALSE_SDD_IDX);
150
151 unique_table.get_mut().insert(tt.id(), tt);
152 unique_table.get_mut().insert(ff.id(), ff);
153
154 let variables: Vec<_> = options
155 .variables
156 .iter()
157 .enumerate()
158 .map(|(idx, variable)| Variable::new(variable, u32::try_from(idx).unwrap()))
159 .collect();
160
161 let manager = SddManager {
162 options: options.clone(),
163 op_cache: RefCell::new(FxHashMap::default()),
164 neg_cache: RefCell::new(FxHashMap::default()),
165 next_idx: RefCell::new(SddId(2)), vtree_manager: RefCell::new(VTreeManager::new(options.vtree_strategy, &variables)),
167 literal_manager: RefCell::new(LiteralManager::new()),
168 rotating: RefCell::new(false),
169 apply_level: RefCell::new(0),
170 gc_stats: RefCell::new(GCStatistics {
171 nodes_collected: 0,
172 gc_triggered: 0,
173 }),
174 unique_table,
175 };
176
177 for variable in variables {
178 let vtree = manager.vtree_manager.borrow().get_variable_vtree(&variable);
179
180 let positive_literal = manager.new_sdd_from_type(
181 SddType::Literal(Literal::new_with_label(
182 Polarity::Positive,
183 variable.clone(),
184 )),
185 vtree.clone(),
186 None,
187 );
188
189 let negative_literal = manager.new_sdd_from_type(
190 SddType::Literal(Literal::new_with_label(
191 Polarity::Negative,
192 variable.clone(),
193 )),
194 vtree.clone(),
195 None,
196 );
197
198 manager.literal_manager.borrow().add_variable(
199 &variable,
200 positive_literal,
201 negative_literal,
202 );
203 }
204
205 manager
206 }
207
208 pub fn from_dimacs(&self, reader: &mut dyn std::io::Read) -> Result<SddRef> {
220 let mut reader = std::io::BufReader::new(reader);
221 let mut dimacs = dimacs::DimacsParser::new(&mut reader);
222
223 let preamble = dimacs.parse_preamble()?;
224 let num_variables = self.literal_manager.borrow().len();
225 if preamble.variables > num_variables {
226 bail!("preamble specifies more variables than those present in the manager",);
227 }
228
229 let mut sdd = self.tautology();
230 let mut i = 0;
231 loop {
232 match dimacs.parse_next_clause()? {
233 None => return Ok(sdd),
234 Some(clause) => {
235 sdd = self.conjoin(&sdd, &clause.to_sdd(self));
236 if sdd.is_false() {
237 return Ok(sdd);
238 }
239
240 if i != 0
241 && self.options.minimize_after != 0
242 && i % self.options.minimize_after == 0
243 {
244 tracing::info!(sdd_id = sdd.id().0, size = sdd.size(), "before minimizing");
245 self.minimize(
246 self.options.minimization_cutoff,
247 &self.options.fragment_heuristic,
248 &sdd,
249 )?;
250 tracing::info!(sdd_id = sdd.id().0, size = sdd.size(), "after minimizing");
251 }
252 i += 1;
253 }
254 }
255 }
256 }
257
258 pub(crate) fn try_get_node(&self, id: SddId) -> Option<SddRef> {
259 self.unique_table.borrow().get(&id).cloned()
260 }
261
262 #[must_use]
268 pub(crate) fn get_node(&self, id: SddId) -> SddRef {
269 self.unique_table
270 .borrow()
271 .get(&id)
272 .unwrap_or_else(|| panic!("SDD {id} is not in the unique_table"))
273 .clone()
274 }
275
276 pub(crate) fn get_nodes_normalized_for(&self, vtree_idx: VTreeIdx) -> Vec<(SddId, SddRef)> {
277 self.unique_table
278 .borrow()
279 .iter()
280 .filter(|(_, sdd)| !sdd.is_constant() && sdd.vtree().unwrap().index() == vtree_idx)
281 .map(|(id, sdd)| (*id, sdd.clone()))
282 .collect()
283 }
284
285 pub(crate) fn remove_node(&self, id: SddId) -> Result<(), ()> {
289 tracing::debug!(id = id.0, "removing node from cache");
290 let entries: Vec<_> = {
291 self.op_cache
292 .borrow()
293 .iter()
294 .filter(|(entry, res)| entry.contains_id(id) || **res == id)
295 .map(|(entry, res)| (entry.clone(), *res))
296 .collect()
297 };
298
299 for (entry, _) in entries {
300 _ = self.op_cache.borrow_mut().remove(&entry).unwrap();
301 }
302
303 match self.unique_table.borrow_mut().remove(&id) {
304 Some(..) => Ok(()),
305 None => Err(()),
306 }
307 }
308
309 fn remove_from_op_cache(&self, ids: &FxHashSet<SddId>) {
311 let mut entries_to_remove = Vec::new();
312 for (fst, snd) in self.neg_cache.borrow().iter() {
313 if ids.contains(fst) || ids.contains(snd) {
314 entries_to_remove.push(*fst);
315 entries_to_remove.push(*snd);
316 }
317 }
318
319 let mut cache = self.neg_cache.borrow_mut();
320 for id in &entries_to_remove {
321 cache.remove(id);
322 }
323
324 let mut entries_to_remove = Vec::new();
325 for (entry @ Entry { fst, snd, .. }, res) in self.op_cache.borrow().iter() {
326 if ids.contains(fst) || ids.contains(snd) || ids.contains(res) {
327 entries_to_remove.push(entry.clone());
328 }
329 }
330
331 let mut cache = self.op_cache.borrow_mut();
332 for entry in &entries_to_remove {
333 cache.remove(entry);
334 }
335
336 let mut unique_table = self.unique_table.borrow_mut();
337 for id in ids {
338 unique_table.remove(id);
339 }
340 }
341
342 pub fn literal(&self, label: &str, polarity: Polarity) -> Option<SddRef> {
345 let (_, variants) = self.literal_manager.borrow().find_by_label(label)?;
346 Some(variants.get(polarity))
347 }
348
349 pub(crate) fn literal_from_idx(&self, idx: VariableIdx, polarity: Polarity) -> SddRef {
356 let Some((_, variants)) = self.literal_manager.borrow().find_by_index(idx) else {
357 panic!("literal with index {idx:?} has not been created!");
358 };
359
360 variants.get(polarity)
361 }
362
363 pub fn tautology(&self) -> SddRef {
369 self.try_get_node(TRUE_SDD_IDX)
370 .expect("True SDD node must be present in the unique table at all times")
371 }
372
373 pub fn contradiction(&self) -> SddRef {
379 self.try_get_node(FALSE_SDD_IDX)
380 .expect("False SDD node must be present in the unique table at all times")
381 }
382
383 #[must_use]
398 #[instrument(skip_all, ret, level = tracing::Level::DEBUG)]
399 pub fn conjoin(&self, fst: &SddRef, snd: &SddRef) -> SddRef {
400 tracing::debug!(fst_id = fst.id().0, snd_id = snd.id().0);
401 if fst == snd {
402 return fst.clone();
403 }
404
405 if fst.is_false() {
406 return fst.clone();
407 }
408
409 if snd.is_false() {
410 return snd.clone();
411 }
412
413 if fst.is_true() {
414 return snd.clone();
415 }
416
417 if snd.is_true() {
418 return fst.clone();
419 }
420
421 if fst.eq_negated(snd, self) {
422 return self.contradiction();
423 }
424
425 self.apply(fst, snd, Operation::Conjoin)
426 }
427
428 #[must_use]
443 #[instrument(skip_all, ret, level = tracing::Level::DEBUG)]
444 pub fn disjoin(&self, fst: &SddRef, snd: &SddRef) -> SddRef {
445 tracing::debug!(fst_id = fst.id().0, snd_id = snd.id().0);
446 if fst == snd {
447 return fst.clone();
448 }
449
450 if fst.is_true() {
451 return fst.clone();
452 }
453
454 if snd.is_true() {
455 return snd.clone();
456 }
457
458 if fst.is_false() {
459 return snd.clone();
460 }
461
462 if snd.is_false() {
463 return fst.clone();
464 }
465
466 if fst.eq_negated(snd, self) {
467 return self.tautology();
468 }
469
470 self.apply(fst, snd, Operation::Disjoin)
471 }
472
473 #[must_use]
490 #[instrument(skip_all, ret, level = tracing::Level::DEBUG)]
491 pub fn negate(&self, fst: &SddRef) -> SddRef {
492 if fst.is_true() {
493 return self.contradiction();
494 }
495 if fst.is_false() {
496 return self.tautology();
497 }
498
499 tracing::debug!(fst_id = fst.id().0);
500 fst.clone().negate(self)
501 }
502
503 #[must_use]
518 #[instrument(skip_all, ret, level = tracing::Level::DEBUG)]
519 pub fn imply(&self, fst: &SddRef, snd: &SddRef) -> SddRef {
520 tracing::debug!(fst_id = fst.id().0, snd_id = snd.id().0);
521 if fst == snd && fst.is_true() {
522 return snd.clone();
523 }
524
525 if fst.is_false() {
526 return self.tautology();
527 }
528
529 if fst.is_true() {
530 return snd.clone();
531 }
532
533 if fst.eq_negated(snd, self) {
534 return snd.clone();
535 }
536
537 self.apply(&fst.negate(self), snd, Operation::Disjoin)
539 }
540
541 #[must_use]
556 #[instrument(skip_all, ret, level = tracing::Level::DEBUG)]
557 pub fn equiv(&self, fst: &SddRef, snd: &SddRef) -> SddRef {
558 tracing::debug!(fst_id = fst.id().0, snd_id = snd.id().0);
559 if fst == snd {
560 return self.tautology();
561 }
562
563 if fst.eq_negated(snd, self) {
564 return self.contradiction();
565 }
566
567 let fst_con = self.conjoin(&fst.negate(self), &snd.negate(self));
569 let snd_con = self.conjoin(fst, snd);
570 self.disjoin(&fst_con, &snd_con)
571 }
572
573 pub fn model_enumeration(&self, sdd: &SddRef) -> Models {
575 let all_variables: BTreeSet<_> = self.literal_manager.borrow().all_variables();
576 let mut models: Vec<BitVec> = Vec::new();
577
578 if sdd.is_true() {
579 let all_variables: Vec<_> = all_variables.iter().cloned().collect();
580 SddManager::expand_models(&mut models, &all_variables);
581 return Models::new(&models, all_variables.clone());
582 } else if !sdd.is_false() {
583 self.model_enumeration_rec(sdd, &mut models);
585
586 let all_variables: BTreeSet<_> = self.literal_manager.borrow().all_variables();
587 let unbound_variables: Vec<_> = all_variables
588 .difference(&self.get_variables(sdd))
589 .cloned()
590 .collect();
591
592 SddManager::expand_models(&mut models, &unbound_variables);
593 }
594
595 Models::new(&models, all_variables.iter().cloned().collect())
596 }
597
598 pub fn model_count(&self, sdd: &SddRef) -> u64 {
604 if sdd.is_true() {
605 let all_variables = self.literal_manager.borrow().len();
606 return 2_u64.pow(u32::try_from(all_variables).unwrap());
607 }
608
609 if sdd.is_false() {
610 return 0;
611 }
612
613 let models = self.model_count_rec(sdd);
614
615 if self.root().index() == sdd.vtree().unwrap().index() {
616 return models;
617 }
618
619 let sdd_variables = self
620 .vtree_manager
621 .borrow()
622 .get_vtree(sdd.vtree().unwrap().index())
623 .unwrap()
624 .0
625 .borrow()
626 .get_variables()
627 .len();
628 let unbound = self.literal_manager.borrow().len() - sdd_variables;
629
630 models * 2_u64.pow(u32::try_from(unbound).unwrap())
631 }
632
633 fn create_fragment(&self, fragment_strategy: &FragmentHeuristic) -> Result<Fragment> {
635 let variables = self.literal_manager.borrow().len();
636 if variables <= 2 {
637 bail!("cannot construct a fragment: SddManager has only {variables} variables");
638 }
639
640 match fragment_strategy {
641 FragmentHeuristic::Custom(fragment) => Ok(fragment.clone()),
642 FragmentHeuristic::Root => {
643 let root = self.root();
644 if root.right_child().unwrap().is_internal() {
645 Ok(Fragment::new(&root, &root.right_child().unwrap()))
646 } else if root.left_child().unwrap().is_internal() {
647 Ok(Fragment::new(&root, &root.left_child().unwrap()))
648 } else {
649 Err(anyhow!("cannot construct fragment from root since neither children are internal nodes"))
650 }
651 }
652 FragmentHeuristic::MostNormalized => {
653 fn find_root(manager: &SddManager, frequencies: &[i32]) -> VTreeRef {
654 let root = frequencies
655 .iter()
656 .enumerate()
657 .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(Ordering::Equal))
658 .map(|(index, _)| index)
659 .unwrap();
660
661 let root = manager
662 .vtree_manager
663 .borrow()
664 .get_vtree(VTreeIdx(u32::try_from(root).unwrap()))
665 .unwrap();
666
667 assert!(root.is_internal());
668
669 root
670 }
671
672 let nodes = 2 * self.options.variables.len() - 1;
675 let mut frequencies = vec![0; nodes];
676 for sdd in self.unique_table.borrow().values() {
677 if sdd.is_constant_or_literal() {
678 continue;
679 }
680 frequencies[sdd.vtree().unwrap().index().0 as usize] += 1;
681 }
682
683 let mut fragment = None;
684 loop {
685 let root = find_root(self, &frequencies);
686 let lc = root.left_child().unwrap();
687 let rc = root.right_child().unwrap();
688
689 if frequencies.iter().all(|freq| *freq == -1) {
690 break;
692 }
693
694 if frequencies[lc.index().0 as usize] > frequencies[rc.index().0 as usize]
695 && lc.is_internal()
696 {
697 fragment = Some((root.clone(), lc.clone()));
698 break;
699 } else if rc.is_internal() {
700 fragment = Some((root.clone(), rc.clone()));
701 break;
702 }
703
704 frequencies[lc.index().0 as usize] = -1;
707 frequencies[rc.index().0 as usize] = -1;
708 frequencies[root.index().0 as usize] = 1;
709 }
710
711 match fragment {
712 Some((root, child)) => Ok(Fragment::new(&root, &child)),
713 None => Err(anyhow!("no suitable fragment found")),
714 }
715 }
716 }
717 }
718
719 #[instrument(skip_all, ret, level = tracing::Level::DEBUG)]
724 pub fn minimize(
725 &self,
726 cut_off: MinimizationCutoff,
727 fragment_strategy: &FragmentHeuristic,
728 reference_sdd: &SddRef,
729 ) -> Result<()> {
730 let mut fragment = match self.create_fragment(fragment_strategy) {
731 Ok(fragment) => fragment,
732 Err(err) => bail!(err.context("could not minize")),
733 };
734
735 tracing::debug!(sdd_id = reference_sdd.id().0, size = reference_sdd.size());
736
737 let init_size = reference_sdd.size();
738 let mut best_i: usize = 0;
739 let mut best_size = init_size;
740 let mut curr_size = init_size;
741 for (i, _) in (0..12).enumerate() {
742 fragment
743 .next(&Direction::Forward, self)
744 .with_context(|| format!("couild not move to {i}th fragment state"))?;
745
746 tracing::debug!(
747 iteration = i,
748 sdd_id = reference_sdd.id().0,
749 size = reference_sdd.size()
750 );
751
752 debug_assert!(reference_sdd.is_trimmed(self));
753 debug_assert!(reference_sdd.is_compressed(self));
754
755 curr_size = reference_sdd.size();
756 if curr_size <= best_size {
757 best_size = curr_size;
760 best_i = i;
761 }
762
763 if SddManager::should_stop_minimizing(cut_off, init_size, curr_size, i) {
764 if best_i == i || curr_size == best_size {
765 return Ok(());
768 }
769 break;
772 }
773 }
774
775 if curr_size == best_size {
777 return Ok(());
778 }
779
780 fragment
781 .rewind(best_i, self)
782 .with_context(|| format!("could not rewind to {best_i}th state"))?;
783 Ok(())
784 }
785
786 fn should_stop_minimizing(
787 cut_off: MinimizationCutoff,
788 init_size: u64,
789 curr_size: u64,
790 curr_iter: usize,
791 ) -> bool {
792 match cut_off {
793 MinimizationCutoff::Iteration(after_iter) => curr_iter >= after_iter,
794 MinimizationCutoff::BreakAfterFirstImprovement => curr_size <= init_size,
795 MinimizationCutoff::Decrease(decrease) => {
796 #[allow(clippy::cast_precision_loss)]
797 let ratio = curr_size as f64 / init_size as f64;
798 ratio - decrease >= f64::EPSILON
799 }
800 MinimizationCutoff::None => false,
801 }
802 }
803
804 pub fn collect_garbage(&self) {
811 let mut removed = FxHashSet::default();
812
813 let roots: FxHashSet<_> = self
814 .unique_table
815 .borrow()
816 .values()
817 .filter(|sdd| {
821 sdd.0.try_borrow().is_ok() && !sdd.is_constant_or_literal() && sdd.strong_count() == 1
822 })
823 .map(SddRef::id)
824 .collect();
825
826 removed.extend(roots.iter());
828
829 for root in &roots {
830 let root = self.get_node(*root);
831
832 let mut queue: Vec<_> = root
833 .elements()
834 .unwrap()
835 .into_iter()
836 .flat_map(|Element { prime, sub }| [prime, sub])
837 .collect();
838 while let Some(sdd) = queue.pop() {
839 if sdd.strong_count() == 3 && !sdd.is_constant_or_literal() {
842 removed.insert(sdd.id());
844
845 queue.extend(
846 sdd.elements()
847 .unwrap()
848 .into_iter()
849 .flat_map(|Element { prime, sub }| [prime, sub])
850 .filter(|sdd| !sdd.is_constant_or_literal()),
851 );
852 }
853 }
854 }
855
856 self.remove_from_op_cache(&removed);
859 self.gc_stats.borrow_mut().collected(removed.len());
860 }
861
862 fn should_collect_garbage(&self) -> bool {
863 matches!(
864 self.options.garbage_collection,
865 GarbageCollection::Automatic
866 ) && *self.apply_level.borrow() == 0
867 }
868
869 #[instrument(skip_all, level = tracing::Level::DEBUG)]
870 fn model_enumeration_rec(&self, sdd: &SddRef, bitvecs: &mut Vec<BitVec>) {
871 tracing::debug!(sdd_id = sdd.id().0);
872 if let Some(ref mut models) = sdd.models() {
874 tracing::debug!("has {} cached models", models.len());
875 bitvecs.append(models);
876 return;
877 }
878
879 if sdd.is_constant() {
880 return;
881 }
882
883 let mut all_models = Vec::new();
884 {
885 let sdd_type = &sdd.0.borrow().sdd_type;
887
888 if let SddType::Literal(ref literal) = sdd_type {
889 let mut model = bitvec![usize, LocalBits; 0; self.literal_manager.borrow().len()];
890 model.set(
891 literal.var_label().index().0 as usize,
892 literal.polarity() == Polarity::Positive,
893 );
894 bitvecs.push(model);
895 return;
896 }
897
898 let SddType::Decision(decision) = sdd_type else {
899 panic!("every other sddType should've been handled ({sdd_type:?})",);
900 };
901
902 let all_variables = self.get_variables(sdd);
903 for Element { prime, sub } in &decision.elements {
904 let mut models = Vec::new();
905
906 if prime.is_false() || sub.is_false() {
907 continue;
908 }
909
910 if prime.is_true() || sub.is_true() {
911 if prime.is_true() {
912 self.model_enumeration_rec(sub, &mut models);
913 } else {
914 self.model_enumeration_rec(prime, &mut models);
915 }
916 } else {
917 let mut fst = Vec::new();
918 let mut snd = Vec::new();
919
920 self.model_enumeration_rec(prime, &mut fst);
921 self.model_enumeration_rec(sub, &mut snd);
922
923 for fst_bv in &fst {
924 for snd_bv in &snd {
925 models.push(fst_bv.clone().bitor(snd_bv));
926 }
927 }
928 }
929
930 let all_reachable_variables = self
931 .get_variables(prime)
932 .union(&self.get_variables(sub))
933 .cloned()
934 .collect();
935 let unbound_variables: Vec<_> = all_variables
936 .difference(&all_reachable_variables)
937 .cloned()
938 .collect();
939
940 SddManager::expand_models(&mut models, &unbound_variables);
941 all_models.append(&mut models);
942 }
943 }
944 bitvecs.append(&mut all_models);
945
946 sdd.cache_models(bitvecs);
947 }
948
949 fn model_count_rec(&self, sdd: &SddRef) -> u64 {
951 if let Some(model_count) = sdd.model_count() {
953 return model_count;
954 }
955
956 let mut total_models = 0;
957 {
958 let SddType::Decision(ref decision) = sdd.0.borrow().sdd_type else {
960 panic!("every other sddType should've been handled");
961 };
962
963 let get_models_count = |sdd: &SddRef| {
964 if sdd.is_literal() || sdd.is_true() {
965 1
966 } else if sdd.is_false() {
967 0
968 } else {
969 self.model_count_rec(sdd)
970 }
971 };
972
973 let all_variables = self.get_variables(sdd).len();
974
975 for Element { prime, sub } in &decision.elements {
976 let model_count = get_models_count(prime) * get_models_count(sub);
977
978 let all_reachable = self
980 .get_variables(prime)
981 .union(&self.get_variables(sub))
982 .count();
983 let unbound_variables = all_variables - all_reachable;
984
985 total_models += model_count * 2_u64.pow(u32::try_from(unbound_variables).unwrap());
986 }
987 }
988
989 sdd.cache_model_count(total_models);
990 total_models
991 }
992
993 pub fn draw_all_sdds(&self, writer: &mut dyn std::io::Write) -> Result<()> {
999 let mut dot_writer = DotWriter::new(String::from("sdd"), true);
1000 for sdd in self.unique_table.borrow().values() {
1001 sdd.draw(&mut dot_writer);
1002 }
1003 dot_writer.write(writer)
1004 }
1005
1006 pub fn draw_sdd(&self, writer: &mut dyn std::io::Write, sdd: &SddRef) -> Result<()> {
1012 let mut dot_writer = DotWriter::new(String::from("sdd"), false);
1013 let mut seen = FxHashSet::default();
1014
1015 let mut sdds = vec![sdd.clone()];
1016 while let Some(sdd) = sdds.pop() {
1017 if seen.contains(&sdd.id()) {
1018 continue;
1019 }
1020
1021 sdd.draw(&mut dot_writer);
1022 seen.insert(sdd.id());
1023
1024 if let SddType::Decision(Decision { ref elements }) = sdd.0.borrow().sdd_type {
1025 elements
1026 .iter()
1027 .map(Element::get_prime_sub)
1028 .for_each(|(prime, sub)| {
1029 sdds.push(prime);
1030 sdds.push(sub);
1031 });
1032 };
1033 }
1034
1035 dot_writer.write(writer)
1036 }
1037
1038 pub fn draw_vtree(&self, writer: &mut dyn std::io::Write) -> Result<()> {
1044 let mut dot_writer = DotWriter::new(String::from("vtree"), false);
1045 self.vtree_manager.borrow().draw(&mut dot_writer);
1046 dot_writer.write(writer)
1047 }
1048
1049 #[must_use]
1051 #[instrument(skip_all, ret, level = tracing::Level::DEBUG)]
1052 fn apply(&self, fst: &SddRef, snd: &SddRef, op: Operation) -> SddRef {
1053 tracing::debug!(fst_id = fst.id().0, snd_id = snd.id().0, ?op, "apply");
1054 let (fst, snd) = if fst.vtree().unwrap().index() < snd.vtree().unwrap().index() {
1055 (fst, snd)
1056 } else {
1057 (snd, fst)
1058 };
1059
1060 if let Some(result) =
1061 self.get_cached_operation(&CachedOperation::BinOp(fst.id(), op, snd.id()))
1062 {
1063 tracing::debug!(fst_id = fst.id().0, snd_id = snd.id().0, ?op, "cached");
1064 return self.get_node(result);
1065 }
1066
1067 let (lca, order) = self
1068 .vtree_manager
1069 .borrow()
1070 .least_common_ancestor(fst.vtree().unwrap().index(), snd.vtree().unwrap().index());
1071
1072 *self.apply_level.borrow_mut() += 1;
1073
1074 let elements = match order {
1075 VTreeOrder::Equal => self._apply_eq(fst, snd, op),
1076 VTreeOrder::Inequal => self._apply_ineq(fst, snd, op),
1077 VTreeOrder::LeftSubOfRight => self._apply_left_sub_of_right(fst, snd, op),
1078 VTreeOrder::RightSubOfLeft => self._apply_right_sub_of_left(fst, snd, op),
1079 };
1080
1081 let sdd = Sdd::unique_d(&elements, &lca, self).canonicalize(self);
1082
1083 debug_assert!(sdd.is_trimmed(self));
1084 debug_assert!(sdd.is_compressed(self));
1085
1086 self.insert_node(&sdd);
1087 self.cache_operation(&CachedOperation::BinOp(fst.id(), op, snd.id()), sdd.id());
1088
1089 *self.apply_level.borrow_mut() -= 1;
1090
1091 if self.should_collect_garbage() {
1092 self.collect_garbage();
1093 }
1094
1095 sdd
1096 }
1097
1098 pub fn gc_statistics(&self) -> GCStatistics {
1100 self.gc_stats.borrow().clone()
1101 }
1102
1103 pub fn total_sdds(&self) -> u64 {
1105 self.unique_table.borrow().len() as u64
1106 }
1107
1108 #[instrument(skip_all, ret, level = tracing::Level::DEBUG)]
1111 fn _apply_eq(&self, fst: &SddRef, snd: &SddRef, op: Operation) -> BTreeSet<Element> {
1112 tracing::debug!(fst_id = fst.id().0, snd_id = snd.id().0, ?op, "apply_eq");
1113 assert_eq!(fst.vtree().unwrap().index(), snd.vtree().unwrap().index());
1114 assert!(!fst.is_constant());
1115 assert!(!snd.is_constant());
1116
1117 let mut elements = BTreeSet::new();
1118 for Element {
1119 prime: fst_prime,
1120 sub: fst_sub,
1121 } in &fst.0.borrow().expand(self).elements
1122 {
1123 for Element {
1124 prime: snd_prime,
1125 sub: snd_sub,
1126 } in &snd.0.borrow().expand(self).elements
1127 {
1128 let res_prime = self.conjoin(fst_prime, snd_prime);
1129
1130 if !res_prime.is_false() {
1131 let res_sub = match op {
1132 Operation::Conjoin => self.conjoin(fst_sub, snd_sub),
1133 Operation::Disjoin => self.disjoin(fst_sub, snd_sub),
1134 };
1135
1136 if res_sub.is_true() && res_prime.is_true() {
1137 println!(
1138 "_apply_eq: we can optimize since res_sub and res_prime are both true"
1139 );
1140 }
1141
1142 elements.insert(Element {
1143 prime: res_prime,
1144 sub: res_sub,
1145 });
1146 }
1147 }
1148 }
1149
1150 elements
1151 }
1152
1153 #[instrument(skip_all, ret, level = tracing::Level::DEBUG)]
1155 fn _apply_ineq(&self, fst: &SddRef, snd: &SddRef, op: Operation) -> BTreeSet<Element> {
1156 tracing::debug!(fst_id = fst.id().0, snd_id = snd.id().0, ?op, "apply_ineq");
1157 assert!(fst.vtree().unwrap().index() < snd.vtree().unwrap().index());
1158 assert!(!fst.is_constant());
1159 assert!(!snd.is_constant());
1160
1161 let fst_negated = fst.negate(self);
1162
1163 let apply = |fst: &SddRef, snd: &SddRef| {
1164 if op == Operation::Conjoin {
1165 self.conjoin(fst, snd)
1166 } else {
1167 self.disjoin(fst, snd)
1168 }
1169 };
1170
1171 let tt = self.tautology();
1172 let ff = self.contradiction();
1173
1174 btreeset!(
1175 Element {
1176 prime: fst.clone(),
1177 sub: apply(snd, &tt),
1178 },
1179 Element {
1180 prime: fst_negated,
1181 sub: apply(snd, &ff),
1182 }
1183 )
1184 }
1185
1186 #[instrument(skip_all, ret, level = tracing::Level::DEBUG)]
1189 fn _apply_left_sub_of_right(
1190 &self,
1191 fst: &SddRef,
1192 snd: &SddRef,
1193 op: Operation,
1194 ) -> BTreeSet<Element> {
1195 tracing::debug!(
1196 fst_id = fst.id().0,
1197 snd_id = snd.id().0,
1198 ?op,
1199 "apply_left_sub_of_right"
1200 );
1201 assert!(fst.vtree().unwrap().index() < snd.vtree().unwrap().index());
1202 assert!(!fst.is_constant());
1203 assert!(!snd.is_constant());
1204
1205 let new_node = if op == Operation::Conjoin {
1206 fst
1207 } else {
1208 &fst.negate(self)
1209 };
1210
1211 let snd_elements = snd.0.borrow().sdd_type.elements().unwrap_or_else(||
1212 panic!(
1213 "snd of _apply_left_sub_of_right must be a decision node but was instead {} (id {})",
1214 snd.0.borrow().sdd_type.name(),
1215 snd.id(),
1216 )
1217 );
1218
1219 let mut elements = btreeset!(Element {
1220 prime: self.negate(new_node),
1221 sub: self.get_node(op.zero()),
1222 });
1223
1224 for Element {
1225 prime: snd_prime,
1226 sub: snd_sub,
1227 } in snd_elements
1228 {
1229 let new_prime = self.conjoin(&snd_prime, new_node);
1230 if !new_prime.is_false() {
1231 elements.insert(Element {
1232 prime: new_prime,
1233 sub: snd_sub,
1234 });
1235 }
1236 }
1237
1238 elements
1239 }
1240 #[instrument(skip_all, ret, level = tracing::Level::DEBUG)]
1243 fn _apply_right_sub_of_left(
1244 &self,
1245 fst: &SddRef,
1246 snd: &SddRef,
1247 op: Operation,
1248 ) -> BTreeSet<Element> {
1249 tracing::debug!(
1250 fst_id = fst.id().0,
1251 snd_id = snd.id().0,
1252 ?op,
1253 "apply_right_sub_of_left"
1254 );
1255 assert!(fst.vtree().unwrap().index() < snd.vtree().unwrap().index());
1256 assert!(!fst.is_constant());
1257 assert!(!snd.is_constant());
1258
1259 let fst_elements = fst.0.borrow().sdd_type.elements().unwrap_or_else(||
1260 panic!(
1261 "fst of _apply_right_sub_of_left must be a decision node but was instead {} (id {})",
1262 fst.0.borrow().sdd_type.name(),
1263 fst.id(),
1264 )
1265 );
1266
1267 let mut elements = BTreeSet::new();
1268
1269 for Element {
1270 prime: fst_prime,
1271 sub: fst_sub,
1272 } in fst_elements
1273 {
1274 let new_sub = match op {
1275 Operation::Conjoin => self.conjoin(&fst_sub, snd),
1276 Operation::Disjoin => self.disjoin(&fst_sub, snd),
1277 };
1278
1279 elements.insert(Element {
1280 prime: fst_prime,
1281 sub: new_sub,
1282 });
1283 }
1284
1285 elements
1286 }
1287
1288 pub(crate) fn insert_node(&self, sdd: &SddRef) {
1290 debug_assert!(sdd.is_trimmed(self));
1291 debug_assert!(sdd.is_compressed(self));
1292
1293 self.unique_table.borrow_mut().insert(sdd.id(), sdd.clone());
1294 }
1295
1296 pub(crate) fn cache_operation(&self, op: &CachedOperation, result: SddId) {
1298 match op {
1299 CachedOperation::BinOp(fst, op, snd) => self.op_cache.borrow_mut().insert(
1300 Entry {
1301 fst: *fst,
1302 snd: *snd,
1303 op: *op,
1304 },
1305 result,
1306 ),
1307 CachedOperation::Neg(fst) => self.neg_cache.borrow_mut().insert(*fst, result),
1308 };
1309 }
1310
1311 pub(crate) fn get_cached_operation(&self, op: &CachedOperation) -> Option<SddId> {
1313 match op {
1314 CachedOperation::BinOp(fst, op, snd) => self
1315 .op_cache
1316 .borrow()
1317 .get(&Entry {
1318 fst: *fst,
1319 snd: *snd,
1320 op: *op,
1321 })
1322 .copied(),
1323 CachedOperation::Neg(fst) => self.neg_cache.borrow().get(fst).copied(),
1324 }
1325 }
1326
1327 fn get_variables(&self, sdd: &SddRef) -> BTreeSet<Variable> {
1329 if sdd.is_constant() {
1330 return BTreeSet::new();
1331 }
1332
1333 self.vtree_manager
1334 .borrow()
1335 .get_vtree(sdd.vtree().unwrap().index())
1336 .unwrap()
1337 .0
1338 .borrow()
1339 .get_variables()
1340 }
1341
1342 fn expand_models(models: &mut Vec<BitVec>, unbound_variables: &[Variable]) {
1344 if unbound_variables.is_empty() {
1345 return;
1346 }
1347
1348 let num_models = models.len();
1349 if unbound_variables.len() == 1 {
1350 let unbound_variable = unbound_variables.first().unwrap();
1351 for i in 0..num_models {
1352 let mut new_model = models.get(i).unwrap().clone();
1353 new_model.set(unbound_variable.index().0 as usize, true);
1354 models.push(new_model);
1355 }
1356
1357 return;
1358 }
1359
1360 for mask in 1..=unbound_variables.len() + 1 {
1361 let variables_to_set: Vec<_> = set_bits_indices(mask)
1362 .iter()
1363 .map(|&idx| unbound_variables.get(idx).unwrap())
1364 .collect();
1365
1366 for i in 0..num_models {
1367 let mut new_model = models.get(i).unwrap().clone();
1368 for variable_to_set in &variables_to_set {
1369 new_model.set(variable_to_set.index().0 as usize, true);
1370 }
1371 models.push(new_model);
1372 }
1373 }
1374 }
1375
1376 pub fn root(&self) -> VTreeRef {
1378 self.vtree_manager.borrow().root()
1379 }
1380
1381 #[instrument(skip_all, ret, level = tracing::Level::DEBUG)]
1406 pub fn rotate_left(&self, x: &VTreeRef) -> Result<()> {
1407 self.rotating.replace(true);
1408
1409 let w =
1410 x.0.borrow()
1411 .get_parent()
1412 .expect("invalid fragment: `x` does not have a parent");
1413
1414 let LeftRotateSplit { bc_vec, c_vec } = split_nodes_for_left_rotate(&w, x, self);
1415
1416 self.vtree_manager.borrow_mut().rotate_left(x)?;
1417
1418 for bc in &bc_vec {
1419 bc.replace_contents(SddType::Decision(Decision {
1420 elements: rotate_partition_left(bc, x, self).elements,
1421 }));
1422 bc.replace_contents(bc.canonicalize(self).0.borrow().sdd_type.clone());
1423 bc.set_vtree(x.clone());
1424
1425 debug_assert!(bc.is_compressed(self));
1426 debug_assert!(bc.is_trimmed(self));
1427 }
1428
1429 self.finalize_vtree_op(&bc_vec, &c_vec, x);
1430 self.invalidate_cached_models();
1431
1432 self.rotating.replace(false);
1433 Ok(())
1434 }
1435
1436 fn finalize_vtree_op(&self, replaced: &[SddRef], moved: &[SddRef], vtree: &VTreeRef) {
1437 for sdd in replaced {
1438 self.insert_node(sdd);
1439 }
1440
1441 for sdd in moved {
1442 sdd.set_vtree(vtree.clone());
1443 self.insert_node(sdd);
1444 }
1445 }
1446
1447 #[instrument(skip_all, ret, level = tracing::Level::DEBUG)]
1472 pub fn rotate_right(&self, x: &VTreeRef) -> Result<()> {
1473 self.rotating.replace(true);
1474
1475 let w = x.left_child().unwrap();
1476 let RightRotateSplit { ab_vec, a_vec } = split_nodes_for_right_rotate(x, &w, self);
1477 self.vtree_manager.borrow_mut().rotate_right(x)?;
1478
1479 for ab in &ab_vec {
1480 ab.replace_contents(SddType::Decision(Decision {
1481 elements: rotate_partition_right(ab, &w, self).elements,
1482 }));
1483 ab.replace_contents(ab.canonicalize(self).0.borrow().sdd_type.clone());
1484 ab.set_vtree(w.clone());
1485
1486 debug_assert!(ab.is_compressed(self));
1487 debug_assert!(ab.is_trimmed(self));
1488 }
1489
1490 self.finalize_vtree_op(&ab_vec, &a_vec, &w);
1491 self.invalidate_cached_models();
1492
1493 self.rotating.replace(false);
1494 Ok(())
1495 }
1496
1497 #[instrument(skip_all, ret, level = tracing::Level::DEBUG)]
1514 pub fn swap(&self, x: &VTreeRef) -> Result<()> {
1515 self.rotating.replace(true);
1516
1517 let split = split_nodes_for_swap(x, self);
1518 self.vtree_manager.borrow_mut().swap(x)?;
1519
1520 for sdd in &split {
1521 let dec = Decision {
1522 elements: swap_partition(sdd, self).elements,
1523 };
1524
1525 if let Some(trimmed) = dec.trim(self) {
1526 sdd.replace_contents(trimmed.0.borrow().sdd_type.clone());
1527 sdd.set_id(trimmed.id());
1528 if !sdd.is_constant() {
1529 sdd.set_vtree(trimmed.vtree().unwrap());
1530 }
1531 } else {
1532 sdd.replace_contents(SddType::Decision(dec));
1533 sdd.set_vtree(x.clone());
1534 }
1535
1536 debug_assert!(sdd.is_compressed(self));
1537 debug_assert!(sdd.is_trimmed(self));
1538 }
1539
1540 self.finalize_vtree_op(&split, &[], x);
1541 self.invalidate_cached_models();
1542
1543 self.rotating.replace(false);
1544
1545 Ok(())
1546 }
1547
1548 fn invalidate_cached_models(&self) {
1549 for (_, sdd) in self.unique_table.borrow().iter() {
1551 sdd.0.borrow_mut().invalidate_cache();
1552 }
1553 }
1554
1555 pub(crate) fn new_sdd_from_type(
1557 &self,
1558 sdd_type: SddType,
1559 vtree: VTreeRef,
1560 negation: Option<SddId>,
1561 ) -> SddRef {
1562 let sdd = SddRef::new(Sdd::new(sdd_type, *self.next_idx.borrow(), vtree));
1563 self.move_idx();
1564 if let Some(negation) = negation {
1565 self.cache_operation(&CachedOperation::Neg(sdd.id()), negation);
1566 }
1567
1568 self.insert_node(&sdd);
1569 sdd
1570 }
1571
1572 pub(crate) fn idx(&self) -> SddId {
1573 *self.next_idx.borrow()
1574 }
1575
1576 pub(crate) fn move_idx(&self) {
1578 let mut idx = self.next_idx.borrow_mut();
1579 *idx += SddId(1);
1580 }
1581}
1582
1583#[cfg(test)]
1584mod test {
1585 #![allow(non_snake_case)]
1586
1587 use super::{SddManager, SddOptions};
1588 use crate::{
1589 literal::{Literal, Polarity, VariableIdx},
1590 manager::{
1591 model::Model,
1592 options::{GarbageCollection, VTreeStrategy},
1593 },
1594 util::quick_draw,
1595 };
1596 use bon::arr;
1597 use pretty_assertions::assert_eq;
1598
1599 #[test]
1600 fn simple_conjoin() {
1601 let options = SddOptions::builder().variables(arr!["a", "b"]).build();
1602 let manager = SddManager::new(&options);
1603
1604 let tt = manager.tautology();
1605 let ff = manager.contradiction();
1606
1607 assert_eq!(tt, manager.conjoin(&tt, &tt));
1608 assert_eq!(ff, manager.conjoin(&tt, &ff));
1609 assert_eq!(ff, manager.conjoin(&ff, &tt));
1610 assert_eq!(ff, manager.conjoin(&ff, &ff));
1611
1612 let lit_a = manager.literal("a", Polarity::Positive).unwrap();
1613 let lit_not_a = manager.literal("a", Polarity::Negative).unwrap();
1614
1615 assert_eq!(ff, manager.conjoin(&lit_a, &lit_not_a));
1616 assert_eq!(ff, manager.conjoin(&lit_not_a, &lit_a));
1617 assert_eq!(lit_a, manager.conjoin(&lit_a, &lit_a));
1618 assert_eq!(lit_not_a, manager.conjoin(&lit_not_a, &lit_not_a));
1619 }
1620
1621 #[test]
1622 fn simple_disjoin() {
1623 let options = SddOptions::builder().variables(arr!["a", "b"]).build();
1624 let manager = SddManager::new(&options);
1625
1626 let tt = manager.tautology();
1627 let ff = manager.contradiction();
1628
1629 assert_eq!(tt, manager.disjoin(&tt, &tt));
1630 assert_eq!(tt, manager.disjoin(&tt, &ff));
1631 assert_eq!(tt, manager.disjoin(&ff, &tt));
1632 assert_eq!(ff, manager.disjoin(&ff, &ff));
1633
1634 let lit_a = manager.literal("a", Polarity::Positive).unwrap();
1635 let lit_not_a = manager.literal("a", Polarity::Negative).unwrap();
1636
1637 assert_eq!(tt, manager.disjoin(&lit_a, &lit_not_a));
1638 assert_eq!(tt, manager.disjoin(&lit_not_a, &lit_a));
1639 assert_eq!(lit_a, manager.disjoin(&lit_a, &lit_a));
1640 assert_eq!(lit_not_a, manager.disjoin(&lit_not_a, &lit_not_a));
1641 }
1642
1643 #[test]
1644 fn simple_negate() {
1645 let options = SddOptions::builder().variables(arr!["a", "b"]).build();
1646 let manager = SddManager::new(&options);
1647
1648 let tt = manager.tautology();
1649 let ff = manager.contradiction();
1650
1651 assert_eq!(ff, manager.negate(&tt));
1652 assert_eq!(tt, manager.negate(&ff));
1653
1654 let lit_a = manager.literal("a", Polarity::Positive).unwrap();
1655 let lit_not_a = manager.literal("a", Polarity::Negative).unwrap();
1656
1657 assert_eq!(lit_a, manager.negate(&lit_not_a));
1658 assert_eq!(lit_not_a, manager.negate(&lit_a));
1659 }
1660
1661 #[test]
1662 fn simple_imply() {
1663 let options = SddOptions::builder().variables(arr!["a", "b"]).build();
1664 let manager = SddManager::new(&options);
1665
1666 let tt = manager.tautology();
1667 let ff = manager.contradiction();
1668
1669 assert_eq!(ff, manager.imply(&tt, &ff));
1670 assert_eq!(tt, manager.imply(&ff, &ff));
1671
1672 let lit_a = manager.literal("a", Polarity::Positive).unwrap();
1673 let lit_not_a = manager.literal("a", Polarity::Negative).unwrap();
1674
1675 assert_eq!(lit_not_a, manager.imply(&lit_a, &lit_not_a));
1677 assert_eq!(lit_a, manager.imply(&lit_not_a, &lit_a));
1679 }
1680
1681 #[test]
1682 fn simple_equiv() {
1683 let options = SddOptions::builder().variables(arr!["a", "b"]).build();
1684 let manager = SddManager::new(&options);
1685
1686 let tt = manager.tautology();
1687 let ff = manager.contradiction();
1688
1689 assert_eq!(ff, manager.equiv(&tt, &ff));
1690 assert_eq!(tt, manager.equiv(&ff, &ff));
1691
1692 let lit_a = manager.literal("a", Polarity::Positive).unwrap();
1693 let lit_not_a = manager.literal("a", Polarity::Negative).unwrap();
1694
1695 assert_eq!(tt, manager.equiv(&lit_a, &lit_a));
1696 assert_eq!(ff, manager.equiv(&lit_a, &lit_not_a));
1697 }
1698
1699 #[test]
1700 fn apply() {
1701 let options = SddOptions::builder()
1702 .variables(arr!["a", "b", "c", "d"])
1703 .garbage_collection(GarbageCollection::Automatic)
1704 .build();
1705 let manager = SddManager::new(&options);
1706
1707 let lit_a = manager.literal("a", Polarity::Positive).unwrap();
1708 let lit_b = manager.literal("b", Polarity::Positive).unwrap();
1709 let lit_d = manager.literal("d", Polarity::Positive).unwrap();
1710 let a_and_d = manager.conjoin(&lit_a, &lit_d);
1719 assert_eq!(a_and_d.vtree().unwrap().index().0, 3);
1720
1721 let a_and_d__and_b = manager.conjoin(&a_and_d, &lit_b);
1723 assert_eq!(a_and_d__and_b.vtree().unwrap().index().0, 3);
1724 }
1725
1726 #[test]
1727 fn model_counting() {
1728 let options = SddOptions::builder()
1729 .variables(arr!["a", "b", "c", "d"])
1730 .build();
1731 let manager = SddManager::new(&options);
1732
1733 let lit_a = manager.literal("a", Polarity::Positive).unwrap();
1734 let lit_b = manager.literal("b", Polarity::Positive).unwrap();
1735 let lit_c = manager.literal("c", Polarity::Positive).unwrap();
1736 let lit_d = manager.literal("d", Polarity::Positive).unwrap();
1737
1738 let a_and_d = manager.conjoin(&lit_a, &lit_d);
1739 assert_eq!(manager.model_count(&a_and_d), 4);
1740
1741 let a_or_d = manager.disjoin(&a_and_d, &lit_a);
1742 assert_eq!(manager.model_count(&a_or_d), manager.model_count(&lit_a));
1743
1744 let a_and_b = manager.conjoin(&lit_a, &lit_b);
1745 assert_eq!(manager.model_count(&a_and_b), 4);
1746
1747 let a_and_b_and_b = manager.conjoin(&a_and_b, &lit_b);
1749 assert_eq!(
1750 manager.model_count(&a_and_b_and_b),
1751 manager.model_count(&a_and_b)
1752 );
1753
1754 let a_and_b_and_c = manager.conjoin(&a_and_b, &lit_c);
1755 assert_eq!(manager.model_count(&a_and_b_and_c), 2);
1756
1757 let a_and_b_and_c_or_d = manager.disjoin(&a_and_b_and_c, &lit_d);
1758 assert_eq!(manager.model_count(&a_and_b_and_c_or_d), 9);
1759
1760 assert_eq!(manager.model_count(&manager.tautology()), 2_u64.pow(4));
1761 assert_eq!(manager.model_count(&manager.contradiction()), 0);
1762 }
1763
1764 #[test]
1765 fn model_enumeration() {
1766 {
1769 let options = SddOptions::builder().variables(arr!["a"]).build();
1770 let manager = SddManager::new(&options);
1771 let lit_a = manager.literal("a", Polarity::Positive).unwrap();
1772
1773 assert_eq!(
1774 manager.model_enumeration(&lit_a).all_models(),
1775 vec![Model::new_from_literals(&[Literal::new(
1776 Polarity::Positive,
1777 "a",
1778 VariableIdx(0)
1779 )])]
1780 );
1781 }
1782
1783 {
1784 let options = SddOptions::builder()
1785 .variables(arr!["a", "b", "c", "d"])
1786 .build();
1787 let manager = SddManager::new(&options);
1788 let lit_a = manager.literal("a", Polarity::Positive).unwrap();
1789 let lit_b = manager.literal("b", Polarity::Positive).unwrap();
1790 let lit_c = manager.literal("c", Polarity::Positive).unwrap();
1791 let lit_d = manager.literal("d", Polarity::Positive).unwrap();
1792
1793 let a_and_b = manager.conjoin(&lit_a, &lit_b);
1794 let models = &[
1795 Model::new_from_literals(&[
1796 Literal::new(Polarity::Positive, "a", VariableIdx(0)),
1797 Literal::new(Polarity::Positive, "b", VariableIdx(1)),
1798 Literal::new(Polarity::Negative, "c", VariableIdx(2)),
1799 Literal::new(Polarity::Negative, "d", VariableIdx(3)),
1800 ]),
1801 Model::new_from_literals(&[
1802 Literal::new(Polarity::Positive, "a", VariableIdx(0)),
1803 Literal::new(Polarity::Positive, "b", VariableIdx(1)),
1804 Literal::new(Polarity::Negative, "c", VariableIdx(2)),
1805 Literal::new(Polarity::Positive, "d", VariableIdx(3)),
1806 ]),
1807 Model::new_from_literals(&[
1808 Literal::new(Polarity::Positive, "a", VariableIdx(0)),
1809 Literal::new(Polarity::Positive, "b", VariableIdx(1)),
1810 Literal::new(Polarity::Positive, "c", VariableIdx(2)),
1811 Literal::new(Polarity::Negative, "d", VariableIdx(3)),
1812 ]),
1813 Model::new_from_literals(&[
1814 Literal::new(Polarity::Positive, "a", VariableIdx(0)),
1815 Literal::new(Polarity::Positive, "b", VariableIdx(1)),
1816 Literal::new(Polarity::Positive, "c", VariableIdx(2)),
1817 Literal::new(Polarity::Positive, "d", VariableIdx(3)),
1818 ]),
1819 ];
1820
1821 assert_eq!(manager.model_enumeration(&a_and_b).all_models(), models);
1822
1823 let a_and_b_and_c = manager.conjoin(&a_and_b, &lit_c);
1824
1825 let models = &[
1826 Model::new_from_literals(&[
1827 Literal::new(Polarity::Positive, "a", VariableIdx(0)),
1828 Literal::new(Polarity::Positive, "b", VariableIdx(1)),
1829 Literal::new(Polarity::Positive, "c", VariableIdx(2)),
1830 Literal::new(Polarity::Negative, "d", VariableIdx(3)),
1831 ]),
1832 Model::new_from_literals(&[
1833 Literal::new(Polarity::Positive, "a", VariableIdx(0)),
1834 Literal::new(Polarity::Positive, "b", VariableIdx(1)),
1835 Literal::new(Polarity::Positive, "c", VariableIdx(2)),
1836 Literal::new(Polarity::Positive, "d", VariableIdx(3)),
1837 ]),
1838 ];
1839
1840 assert_eq!(
1841 manager.model_enumeration(&a_and_b_and_c).all_models(),
1842 models
1843 );
1844
1845 let a_and_b_and_c_and_d = manager.conjoin(&a_and_b_and_c, &lit_d);
1846 let models = &[Model::new_from_literals(&[
1847 Literal::new(Polarity::Positive, "a", VariableIdx(0)),
1848 Literal::new(Polarity::Positive, "b", VariableIdx(1)),
1849 Literal::new(Polarity::Positive, "c", VariableIdx(2)),
1850 Literal::new(Polarity::Positive, "d", VariableIdx(3)),
1851 ])];
1852 assert_eq!(
1853 manager.model_enumeration(&a_and_b_and_c_and_d).all_models(),
1854 models,
1855 );
1856
1857 let not_a = manager.literal("a", Polarity::Negative).unwrap();
1858 let ff = manager.conjoin(¬_a, &a_and_b_and_c_and_d);
1859 assert_eq!(manager.model_enumeration(&ff).all_models(), vec![]);
1860 }
1861 }
1862
1863 #[test]
1864 fn left_rotation() {
1865 let options = SddOptions::builder()
1866 .vtree_strategy(VTreeStrategy::RightLinear)
1867 .garbage_collection(GarbageCollection::Automatic)
1868 .variables(arr!["a", "b", "c", "d"])
1869 .build();
1870 let manager = SddManager::new(&options);
1871
1872 let lit_a = manager.literal("a", Polarity::Positive).unwrap();
1873 let lit_b = manager.literal("b", Polarity::Positive).unwrap();
1874 let lit_c = manager.literal("c", Polarity::Positive).unwrap();
1875 let lit_d = manager.literal("d", Polarity::Positive).unwrap();
1876
1877 let a_and_d = manager.conjoin(&lit_a, &lit_d);
1878 let a_and_d_and_b = manager.conjoin(&a_and_d, &lit_b);
1879 let a_and_d_and_b_and_c = manager.conjoin(&a_and_d_and_b, &lit_c);
1880 let models_before = manager.model_enumeration(&a_and_d_and_b_and_c);
1881
1882 manager
1884 .rotate_left(&manager.root().right_child().unwrap())
1885 .unwrap();
1886
1887 let models_after = manager.model_enumeration(&a_and_d_and_b_and_c);
1888 assert_eq!(models_before, models_after);
1889 }
1890
1891 #[test]
1892 fn right_rotation() {
1893 let options = SddOptions::builder()
1894 .vtree_strategy(VTreeStrategy::LeftLinear)
1895 .garbage_collection(GarbageCollection::Automatic)
1896 .variables(arr!["a", "b", "c", "d"])
1897 .build();
1898 let manager = SddManager::new(&options);
1899
1900 let lit_a = manager.literal("a", Polarity::Positive).unwrap();
1901 let lit_b = manager.literal("b", Polarity::Positive).unwrap();
1902 let lit_c = manager.literal("c", Polarity::Positive).unwrap();
1903 let lit_d = manager.literal("d", Polarity::Positive).unwrap();
1904
1905 let a_and_d = manager.conjoin(&lit_a, &lit_d);
1906 let a_and_d_and_b = manager.conjoin(&a_and_d, &lit_b);
1907 let a_and_d_and_b_and_c = manager.conjoin(&a_and_d_and_b, &lit_c);
1908 let models_before = manager.model_enumeration(&a_and_d_and_b_and_c);
1909
1910 manager.rotate_right(&manager.root()).unwrap();
1912
1913 let models_after = manager.model_enumeration(&a_and_d_and_b_and_c);
1914 assert_eq!(models_before, models_after);
1915 }
1916
1917 #[test]
1918 fn swap() {
1919 let options = SddOptions::builder()
1920 .vtree_strategy(VTreeStrategy::Balanced)
1921 .garbage_collection(GarbageCollection::Automatic)
1922 .variables(arr!["a", "b", "c", "d"])
1923 .build();
1924 let manager = SddManager::new(&options);
1925
1926 let lit_a = manager.literal("a", Polarity::Positive).unwrap();
1927 let lit_b = manager.literal("b", Polarity::Positive).unwrap();
1928 let lit_c = manager.literal("c", Polarity::Positive).unwrap();
1929 let lit_d = manager.literal("d", Polarity::Positive).unwrap();
1930
1931 let a_and_d = manager.conjoin(&lit_a, &lit_d);
1932 let a_and_d_and_b = manager.conjoin(&a_and_d, &lit_b);
1933 let a_and_d_and_b_and_c = manager.conjoin(&a_and_d_and_b, &lit_c);
1934 quick_draw(&manager, &a_and_d_and_b_and_c, "easy");
1935 assert_eq!(a_and_d_and_b_and_c.size(), 8);
1936
1937 let models_before = manager.model_enumeration(&a_and_d_and_b_and_c);
1938
1939 manager.swap(&manager.root()).unwrap();
1940
1941 let models_after = manager.model_enumeration(&a_and_d_and_b_and_c);
1942 assert_eq!(models_before, models_after);
1943 }
1944}