1use std::{fmt::Debug, ops};
17
18#[cfg(feature = "cudd")]
19use cudd::{CuddDD, CuddManager};
20#[cfg(feature = "oxidd")]
21use oxidd::{OxiDD, OxiddManager};
22#[cfg(feature = "config_deserialize")]
23use serde::Deserialize;
24
25#[cfg(feature = "cudd")]
27#[allow(unsafe_code)]
28mod cudd;
29
30#[cfg(feature = "oxidd")]
32mod oxidd;
33
34#[derive(Debug, Clone, PartialEq)]
44pub enum BDD {
45 #[cfg(feature = "cudd")]
47 CuDD(CuddDD),
48 #[cfg(feature = "oxidd")]
50 OxiDD(OxiDD),
51}
52
53impl Bdd for BDD {
54 fn not(&self) -> Self {
55 match self {
56 #[cfg(feature = "cudd")]
57 BDD::CuDD(dd) => BDD::CuDD(dd.not()),
58 #[cfg(feature = "oxidd")]
59 BDD::OxiDD(dd) => BDD::OxiDD(dd.not()),
60 }
61 }
62
63 #[allow(unreachable_patterns)]
64 fn and(&self, rhs: &Self) -> Self {
65 match self {
66 #[cfg(feature = "cudd")]
67 BDD::CuDD(dd) => match rhs {
68 BDD::CuDD(rhs_dd) => BDD::CuDD(dd.and(rhs_dd)),
69 _ => panic!("BDDs are from different managers"),
70 },
71 #[cfg(feature = "oxidd")]
72 BDD::OxiDD(dd) => match rhs {
73 BDD::OxiDD(rhs_dd) => BDD::OxiDD(dd.and(rhs_dd)),
74 _ => panic!("BDDs are from different managers"),
75 },
76 }
77 }
78
79 #[allow(unreachable_patterns)]
80 fn or(&self, rhs: &Self) -> Self {
81 match self {
82 #[cfg(feature = "cudd")]
83 BDD::CuDD(dd) => match rhs {
84 BDD::CuDD(rhs_dd) => BDD::CuDD(dd.or(rhs_dd)),
85 _ => panic!("BDDs are from different managers"),
86 },
87 #[cfg(feature = "oxidd")]
88 BDD::OxiDD(dd) => match rhs {
89 BDD::OxiDD(rhs_dd) => BDD::OxiDD(dd.or(rhs_dd)),
90 _ => panic!("BDDs are from different managers"),
91 },
92 }
93 }
94
95 #[allow(unreachable_patterns)]
96 fn exists<'a, I: IntoIterator<Item = &'a Self>>(&'a self, vars: I) -> Self {
97 match self {
98 #[cfg(feature = "cudd")]
99 BDD::CuDD(dd) => BDD::CuDD(dd.exists(vars.into_iter().map(|v| match v {
100 BDD::CuDD(v) => v,
101 _ => panic!("BDDs are from different managers"),
102 }))),
103 #[cfg(feature = "oxidd")]
104 BDD::OxiDD(dd) => BDD::OxiDD(dd.exists(vars.into_iter().map(|v| match v {
105 BDD::OxiDD(v) => v,
106 _ => panic!("BDDs are from different managers"),
107 }))),
108 }
109 }
110
111 fn satisfiable(&self) -> bool {
112 match self {
113 #[cfg(feature = "cudd")]
114 BDD::CuDD(dd) => dd.satisfiable(),
115 #[cfg(feature = "oxidd")]
116 BDD::OxiDD(dd) => dd.satisfiable(),
117 }
118 }
119
120 #[allow(unreachable_patterns)]
121 fn implies(&self, rhs: &Self) -> Self {
122 match self {
123 #[cfg(feature = "cudd")]
124 BDD::CuDD(dd) => match rhs {
125 BDD::CuDD(rhs_dd) => BDD::CuDD(dd.implies(rhs_dd)),
126 _ => panic!("BDDs are from different managers"),
127 },
128 #[cfg(feature = "oxidd")]
129 BDD::OxiDD(dd) => match rhs {
130 BDD::OxiDD(rhs_dd) => BDD::OxiDD(dd.implies(rhs_dd)),
131 _ => panic!("BDDs are from different managers"),
132 },
133 }
134 }
135
136 #[allow(unreachable_patterns)]
137 fn equiv(&self, rhs: &Self) -> Self {
138 match self {
139 #[cfg(feature = "cudd")]
140 BDD::CuDD(dd) => match rhs {
141 BDD::CuDD(rhs_dd) => BDD::CuDD(dd.equiv(rhs_dd)),
142 _ => panic!("BDDs are from different managers"),
143 },
144 #[cfg(feature = "oxidd")]
145 BDD::OxiDD(dd) => match rhs {
146 BDD::OxiDD(rhs_dd) => BDD::OxiDD(dd.equiv(rhs_dd)),
147 _ => panic!("BDDs are from different managers"),
148 },
149 }
150 }
151
152 #[allow(unreachable_patterns)]
153 fn swap<'a, I: IntoIterator<Item = &'a Self>>(&'a self, from: I, to: I) -> Self {
154 match self {
155 #[cfg(feature = "cudd")]
156 BDD::CuDD(dd) => {
157 let to_cudd = |v: &'a BDD| match v {
158 BDD::CuDD(v) => v,
159 _ => panic!("BDDs are from different managers"),
160 };
161
162 let to = to.into_iter().map(to_cudd);
163 let from = from.into_iter().map(to_cudd);
164
165 BDD::CuDD(dd.swap(from, to))
166 }
167 #[cfg(feature = "oxidd")]
168 BDD::OxiDD(dd) => {
169 let to_oxidd = |v: &'a BDD| match v {
170 BDD::OxiDD(v) => v,
171 _ => panic!("BDDs are from different managers"),
172 };
173
174 let to = to.into_iter().map(to_oxidd);
175 let from = from.into_iter().map(to_oxidd);
176
177 BDD::OxiDD(dd.swap(from, to))
178 }
179 }
180 }
181}
182
183impl ops::Not for &BDD {
184 type Output = BDD;
185
186 fn not(self) -> BDD {
187 Bdd::not(self)
188 }
189}
190
191impl ops::Not for BDD {
192 type Output = BDD;
193
194 fn not(self) -> BDD {
195 Bdd::not(&self)
196 }
197}
198
199impl ops::BitAnd for &BDD {
200 type Output = BDD;
201
202 fn bitand(self, rhs: Self) -> BDD {
203 Bdd::and(self, rhs)
204 }
205}
206
207impl ops::BitAnd for BDD {
208 type Output = BDD;
209
210 fn bitand(self, rhs: Self) -> BDD {
211 Bdd::and(&self, &rhs)
212 }
213}
214
215impl ops::BitAndAssign for BDD {
216 fn bitand_assign(&mut self, rhs: Self) {
217 *self = Bdd::and(self, &rhs);
218 }
219}
220
221impl ops::BitOr for &BDD {
222 type Output = BDD;
223
224 fn bitor(self, rhs: Self) -> BDD {
225 Bdd::or(self, rhs)
226 }
227}
228
229impl ops::BitOr for BDD {
230 type Output = BDD;
231
232 fn bitor(self, rhs: Self) -> BDD {
233 Bdd::or(&self, &rhs)
234 }
235}
236
237impl ops::BitOrAssign for BDD {
238 fn bitor_assign(&mut self, rhs: Self) {
239 *self = Bdd::or(self, &rhs);
240 }
241}
242
243#[derive(Debug, Clone, PartialEq)]
247#[cfg_attr(feature = "config_deserialize", derive(Deserialize))]
248pub enum BDDManagerConfig {
249 #[cfg(feature = "cudd")]
251 Cudd(cudd::CuddManagerConfig),
252 #[cfg(feature = "oxidd")]
254 Oxidd(oxidd::OxiddManagerConfig),
255}
256
257impl BDDManagerConfig {
258 pub fn mgr_from_config(&self) -> BDDManager {
260 match self {
261 #[cfg(feature = "cudd")]
262 BDDManagerConfig::Cudd(cfg) => BDDManager::new_cudd_with_config(cfg),
263 #[cfg(feature = "oxidd")]
264 BDDManagerConfig::Oxidd(cfg) => BDDManager::new_oxidd_with_config(cfg),
265 }
266 }
267
268 #[cfg(feature = "cudd")]
270 pub fn new_cudd() -> Self {
271 BDDManagerConfig::Cudd(cudd::CuddManagerConfig::default())
272 }
273
274 #[cfg(feature = "oxidd")]
276 pub fn new_oxidd() -> Self {
277 BDDManagerConfig::Oxidd(oxidd::OxiddManagerConfig::default())
278 }
279}
280
281#[derive(Debug, Clone, PartialEq)]
288pub enum BDDManager {
289 #[cfg(feature = "cudd")]
291 CuDD(CuddManager),
292 #[cfg(feature = "oxidd")]
294 OxiDD(OxiddManager),
295}
296
297impl BDDManager {
298 pub fn new(cfg: &BDDManagerConfig) -> Self {
300 cfg.mgr_from_config()
301 }
302
303 #[cfg(feature = "oxidd")]
305 pub fn new_oxidd() -> Self {
306 BDDManager::OxiDD(OxiddManager::default())
307 }
308
309 #[cfg(feature = "oxidd")]
311 pub fn new_oxidd_with_config(cfg: &oxidd::OxiddManagerConfig) -> Self {
312 BDDManager::OxiDD(OxiddManager::new_with_config(cfg))
313 }
314
315 #[cfg(feature = "cudd")]
317 pub fn new_cudd() -> Self {
318 BDDManager::CuDD(CuddManager::default())
319 }
320
321 #[cfg(feature = "cudd")]
323 pub fn new_cudd_with_config(cfg: &cudd::CuddManagerConfig) -> Self {
324 BDDManager::CuDD(CuddManager::new_with_config(cfg))
325 }
326}
327
328impl Default for BDDManager {
329 #[allow(unreachable_code)]
333 fn default() -> Self {
334 #[cfg(feature = "cudd")]
335 return BDDManager::CuDD(CuddManager::default());
336 #[cfg(feature = "oxidd")]
337 return BDDManager::OxiDD(OxiddManager::default());
338 panic!("No BDD library enabled during compilation");
339 }
340}
341
342impl BddManager for BDDManager {
343 type DD = BDD;
344
345 fn new_var(&mut self) -> Self::DD {
346 match self {
347 #[cfg(feature = "cudd")]
348 BDDManager::CuDD(mgr) => BDD::CuDD(mgr.new_var()),
349 #[cfg(feature = "oxidd")]
350 BDDManager::OxiDD(mgr) => BDD::OxiDD(mgr.new_var()),
351 }
352 }
353
354 fn get_bdd_false(&self) -> Self::DD {
355 match self {
356 #[cfg(feature = "cudd")]
357 BDDManager::CuDD(mgr) => BDD::CuDD(mgr.get_bdd_false()),
358 #[cfg(feature = "oxidd")]
359 BDDManager::OxiDD(mgr) => BDD::OxiDD(mgr.get_bdd_false()),
360 }
361 }
362
363 fn get_bdd_true(&self) -> Self::DD {
364 match self {
365 #[cfg(feature = "cudd")]
366 BDDManager::CuDD(mgr) => BDD::CuDD(mgr.get_bdd_true()),
367 #[cfg(feature = "oxidd")]
368 BDDManager::OxiDD(mgr) => BDD::OxiDD(mgr.get_bdd_true()),
369 }
370 }
371
372 #[cfg(feature = "visualize")]
373 fn dump_dot<'a, VN, BN>(
374 &'a self,
375 file: std::fs::File,
376 bdd_names: impl IntoIterator<Item = (&'a Self::DD, BN)>,
377 variable_names: impl IntoIterator<Item = (&'a Self::DD, VN)>,
378 ) -> std::io::Result<()>
379 where
380 VN: std::fmt::Display + Clone,
381 BN: std::fmt::Display,
382 {
383 match self {
384 #[cfg(feature = "cudd")]
385 BDDManager::CuDD(mgr) => mgr.dump_dot(
386 file,
387 bdd_names.into_iter().map(|(bdd, name)| match bdd {
388 BDD::CuDD(dd) => (dd, name),
389 _ => panic!("BDDs are from different managers"),
390 }),
391 variable_names.into_iter().map(|(bdd, name)| match bdd {
392 BDD::CuDD(dd) => (dd, name),
393 _ => panic!("BDDs are from different managers"),
394 }),
395 ),
396 #[cfg(feature = "oxidd")]
397 BDDManager::OxiDD(mgr) => mgr.dump_dot(
398 file,
399 bdd_names.into_iter().map(|(bdd, name)| match bdd {
400 BDD::OxiDD(dd) => (dd, name),
401 _ => panic!("BDDs are from different managers"),
402 }),
403 variable_names.into_iter().map(|(bdd, name)| match bdd {
404 BDD::OxiDD(dd) => (dd, name),
405 _ => panic!("BDDs are from different managers"),
406 }),
407 ),
408 }
409 }
410}
411
412pub trait Bdd: Sized + Debug + PartialEq + Clone {
414 fn not(&self) -> Self;
416
417 fn and(&self, rhs: &Self) -> Self;
419
420 fn or(&self, rhs: &Self) -> Self;
422
423 fn exists<'a, I: IntoIterator<Item = &'a Self>>(&'a self, vars: I) -> Self;
425
426 fn satisfiable(&self) -> bool;
428
429 fn implies(&self, rhs: &Self) -> Self;
431
432 fn equiv(&self, rhs: &Self) -> Self;
434
435 fn swap<'a, I: IntoIterator<Item = &'a Self>>(&'a self, from: I, to: I) -> Self;
437}
438
439pub trait BddManager: Debug + PartialEq + Clone + Default {
442 type DD: Bdd;
444
445 fn new_var(&mut self) -> Self::DD;
447
448 fn get_bdd_false(&self) -> Self::DD;
450
451 fn get_bdd_true(&self) -> Self::DD;
453
454 #[cfg(feature = "visualize")]
455 fn dump_dot<'a, VN, BN>(
490 &'a self,
491 file: std::fs::File,
492 bdd_names: impl IntoIterator<Item = (&'a Self::DD, BN)>,
493 variable_names: impl IntoIterator<Item = (&'a Self::DD, VN)>,
494 ) -> std::io::Result<()>
495 where
496 VN: std::fmt::Display + Clone,
497 BN: std::fmt::Display;
498}
499
500pub trait ProvidesBDDManager {
505 fn get_new_bdd_manager(&self) -> BDDManager;
507}
508
509impl ProvidesBDDManager for BDDManager {
510 fn get_new_bdd_manager(&self) -> BDDManager {
511 self.clone()
512 }
513}
514
515#[cfg(test)]
518mod bdd_test_utils {
519
520 use crate::Bdd;
521
522 use super::BddManager;
523
524 pub(crate) fn test_mgr_eq_and_clone<T: BddManager>(mgr1: T, mgr2: T) {
526 assert_ne!(mgr1, mgr2);
527 assert_eq!(mgr1, mgr1.clone());
528 }
529
530 pub(crate) fn test_mgr_get<T: BddManager>(mut mgr: T) {
531 let f = mgr.get_bdd_false();
532 assert!(!f.satisfiable());
533
534 let t = mgr.get_bdd_true();
535 assert!(t.satisfiable());
536
537 let var = mgr.new_var();
538 assert!(var.satisfiable())
539 }
540
541 pub(crate) fn test_not<T: BddManager>(mut mgr: T) {
542 let f = mgr.get_bdd_false();
543 assert!(f.not().satisfiable());
544
545 let t = mgr.get_bdd_true();
546 assert!(!t.not().satisfiable());
547
548 let var = mgr.new_var();
549 assert!(var.not().satisfiable());
550 }
551
552 pub(crate) fn test_and<T: BddManager>(mut mgr: T) {
553 let f = mgr.get_bdd_false();
554 let t = mgr.get_bdd_true();
555
556 assert!(t.and(&t).satisfiable());
557 assert!(!t.and(&f).satisfiable());
558 assert!(!f.and(&t).satisfiable());
559 assert!(!f.and(&f).satisfiable());
560
561 let var = mgr.new_var();
562 assert!(t.and(&var).satisfiable());
563 assert!(var.and(&t).satisfiable());
564
565 assert!(!f.and(&var).satisfiable());
566 assert!(!var.and(&f).satisfiable());
567 }
568
569 pub(crate) fn test_or<T: BddManager>(mut mgr: T) {
570 let f = mgr.get_bdd_false();
571 let t = mgr.get_bdd_true();
572
573 assert!(t.or(&t).satisfiable());
574 assert!(t.or(&f).satisfiable());
575 assert!(f.or(&t).satisfiable());
576 assert!(!f.or(&f).satisfiable());
577
578 let var = mgr.new_var();
579 assert!(t.or(&var).satisfiable());
580 assert!(var.or(&t).satisfiable());
581
582 assert!(f.or(&var).satisfiable());
583 assert!(var.or(&f).satisfiable());
584 }
585
586 pub(crate) fn test_exists<T: BddManager>(mut mgr: T) {
587 let var0 = mgr.new_var();
588 let var1 = mgr.new_var();
589 let var2 = mgr.new_var();
590 let var3 = mgr.new_var();
591
592 let con = var0.and(&var1).and(&var3);
593 assert_eq!(con.exists(vec![&var0, &var1, &var2]), var3)
594 }
595
596 pub(crate) fn test_implies<T: BddManager>(mut mgr: T) {
597 let var1 = mgr.new_var();
598
599 let con1 = &var1;
600 let con2 = &var1.not();
601
602 assert_eq!(con1.implies(con2).and(&var1), mgr.get_bdd_false());
603
604 assert_eq!(con1.implies(&con2.not()), mgr.get_bdd_true());
605 }
606
607 pub(crate) fn test_equiv<T: BddManager>(mut mgr: T) {
608 let var0 = mgr.new_var();
609 let var1 = mgr.new_var();
610 let var2 = mgr.new_var();
611 let var3 = mgr.new_var();
612
613 let con1 = var0.and(&var1).and(&var2);
614 let con2 = var0.and(&var3).and(&var2);
615
616 assert!(con1.equiv(&con2).satisfiable());
617
618 let con3 = var0.and(&var1).and(&var2);
619 let con4 = var0.and(&var1).and(&var2);
620
621 assert_eq!(con3.equiv(&con4), mgr.get_bdd_true());
622
623 let con5 = &var1;
624 let con6 = &var1.not();
625 assert_eq!(con5.equiv(con6), mgr.get_bdd_false());
626 }
627
628 pub(crate) fn test_swap<T: BddManager>(mut mgr: T) {
629 let var0 = mgr.new_var();
630 let var1 = mgr.new_var();
631 let var2 = mgr.new_var();
632 let var3 = mgr.new_var();
633
634 let con1 = var0.and(&var1).and(&var2);
635 let expected_con = var0.and(&var3).and(&var2);
636
637 assert_eq!(con1.swap(vec![&var1], vec![&var3]), expected_con);
638 }
639
640 pub(crate) fn test_swap_behavior_primed_var_in_bdd<T: BddManager>(mut mgr: T) {
641 let l_0 = mgr.new_var();
642 let l_0_prime = mgr.new_var();
643
644 let r_0 = mgr.new_var();
646
647 let test_bdd = l_0.and(&r_0.and(&l_0_prime.not()));
648 let test_bdd = test_bdd.swap(&vec![l_0.clone()], &vec![l_0_prime.clone()]);
649
650 let unexpected_bdd = l_0_prime.and(&r_0.and(&l_0_prime.not()));
651 assert_ne!(test_bdd, unexpected_bdd);
652
653 let expected_bdd = l_0_prime.and(&r_0.and(&l_0.not()));
654 assert_eq!(test_bdd, expected_bdd)
655 }
656
657 pub(crate) fn panic_on_bdd_of_mixed_mgrs<T: BddManager>(mut mgr1: T, mut mgr2: T) {
660 let bdd1 = mgr1.new_var();
661 let bdd2 = mgr2.new_var();
662
663 bdd1.and(&bdd2);
664 }
665}
666
667#[cfg(test)]
668mod test {
669 use super::*;
670 use bdd_test_utils::*;
671
672 fn test_all_functions(f: impl Fn() -> BDDManager) {
673 let mgr1 = f();
674 let mgr2 = f();
675
676 test_mgr_eq_and_clone(mgr1, mgr2);
677
678 let mgr = f();
679 test_mgr_get::<BDDManager>(mgr);
680
681 let mgr = f();
682 test_not(mgr);
683
684 let mgr = f();
685 test_and(mgr);
686
687 let mgr = f();
688 test_or(mgr);
689
690 let mgr = f();
691 test_exists(mgr);
692
693 let mgr = f();
694 test_implies(mgr);
695
696 let mgr = f();
697 test_equiv(mgr);
698
699 let mgr = f();
700 test_swap(mgr);
701
702 let mgr = f();
703 test_swap_behavior_primed_var_in_bdd(mgr);
704 }
705
706 fn test_operators(f: impl Fn() -> BDDManager) {
707 let mut mgr = f();
708
709 let bdd1 = mgr.new_var();
711 let bdd2 = mgr.new_var();
712
713 assert_eq!(!&bdd1, bdd1.not());
715
716 assert_eq!(&bdd1 & &bdd2, bdd1.and(&bdd2));
718 assert_eq!(bdd1.clone() & bdd2.clone(), bdd1.and(&bdd2));
719 let mut bdd3 = bdd1.clone();
720 bdd3 &= bdd2.clone();
721 assert_eq!(bdd3, bdd1.and(&bdd2));
722
723 assert_eq!(&bdd1 | &bdd2, bdd1.or(&bdd2));
725 assert_eq!(bdd1.clone() | bdd2.clone(), bdd1.or(&bdd2));
726 let mut bdd3 = bdd1.clone();
727 bdd3 |= bdd2.clone();
728 assert_eq!(bdd3, bdd1.or(&bdd2));
729 }
730
731 #[cfg(feature = "cudd")]
732 #[test]
733 fn test_cudd_functional() {
734 test_all_functions(BDDManager::new_cudd);
735 test_all_functions(|| BDDManagerConfig::new_cudd().mgr_from_config());
736 test_all_functions(|| BDDManager::new(&BDDManagerConfig::new_cudd()));
737 test_all_functions(BDDManager::default);
738 test_operators(BDDManager::new_cudd);
739 }
740
741 #[cfg(feature = "oxidd")]
742 #[test]
743 fn test_oxidd_functional() {
744 test_all_functions(BDDManager::new_oxidd);
745 test_all_functions(|| BDDManagerConfig::new_oxidd().mgr_from_config());
746 test_all_functions(|| BDDManager::new(&BDDManagerConfig::new_oxidd()));
747 test_all_functions(BDDManager::default);
748 test_operators(BDDManager::new_oxidd);
749 }
750
751 #[test]
752 #[cfg(all(feature = "oxidd", feature = "cudd"))]
753 #[should_panic]
754 fn test_panic_on_bdd_of_mixed_mgrs() {
755 let mgr1 = BDDManager::new_cudd();
756 let mgr2 = BDDManager::new_oxidd();
757 panic_on_bdd_of_mixed_mgrs(mgr1, mgr2);
758 }
759}