1use oxidd::{
9 BooleanFunction, BooleanFunctionQuant, FunctionSubst, Manager, ManagerRef, Subst,
10 bdd::{BDDFunction, BDDManagerRef},
11};
12use std::{fmt::Debug, rc::Rc, sync::Mutex};
13
14use super::{Bdd, BddManager};
15
16#[cfg(feature = "config_deserialize")]
17use serde::Deserialize;
18
19const INNER_NODE_CAPACITY: usize = 1 << 20;
22const APPLY_CACHE_CAPACITY: usize = 1024;
24const THREADS: u32 = 8;
26
27#[derive(Clone)]
29pub struct OxiDD {
30 bdd: oxidd::bdd::BDDFunction,
32 var_id: Option<u32>,
34}
35
36impl Debug for OxiDD {
37 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38 write!(f, "OxiDD does not support prints")
39 }
40}
41
42impl Bdd for OxiDD {
43 fn not(&self) -> Self {
44 OxiDD {
45 bdd: self
46 .bdd
47 .not()
48 .expect("OxiDD ran out of memory. Consider trying again with increased capacity"),
49 var_id: None,
50 }
51 }
52
53 fn and(&self, rhs: &Self) -> Self {
54 OxiDD {
55 bdd: self
56 .bdd
57 .and(&rhs.bdd)
58 .expect("OxiDD ran out of memory. Consider trying again with increased capacity"),
59 var_id: None,
60 }
61 }
62
63 fn or(&self, rhs: &Self) -> Self {
64 OxiDD {
65 bdd: self
66 .bdd
67 .or(&rhs.bdd)
68 .expect("OxiDD ran out of memory. Consider trying again with increased capacity"),
69 var_id: None,
70 }
71 }
72
73 fn exists<'a, I: IntoIterator<Item = &'a Self>>(&'a self, vars: I) -> Self {
74 let mut vars = vars.into_iter().map(|dd| dd.bdd.clone());
75
76 let first = vars.next();
77 if first.is_none() {
78 panic!("No variable for existential quantification");
79 }
80 let first =
81 first.expect("OxiDD ran out of memory. Consider trying again with increased capacity");
82
83 let vars = vars.fold(first, |acc, x| {
84 acc.and(&x)
85 .expect("OxiDD ran out of memory. Consider trying again with increased capacity")
86 });
87
88 OxiDD {
89 bdd: self
90 .bdd
91 .exists(&vars)
92 .expect("OxiDD ran out of memory. Consider trying again with increased capacity"),
93 var_id: None,
94 }
95 }
96
97 fn satisfiable(&self) -> bool {
98 self.bdd.satisfiable()
99 }
100
101 fn implies(&self, rhs: &Self) -> Self {
102 OxiDD {
103 bdd: self
104 .bdd
105 .imp(&rhs.bdd)
106 .expect("OxiDD ran out of memory. Consider trying again with increased capacity"),
107 var_id: None,
108 }
109 }
110
111 fn equiv(&self, rhs: &Self) -> Self {
112 OxiDD {
113 bdd: self
114 .bdd
115 .equiv(&rhs.bdd)
116 .expect("OxiDD ran out of memory. Consider trying again with increased capacity"),
117 var_id: None,
118 }
119 }
120
121 fn swap<'a, I: IntoIterator<Item = &'a Self>>(&'a self, from: I, to: I) -> Self {
122 let mut vars = Vec::new();
123 let mut replacements = Vec::new();
124
125 from.into_iter().zip(to).for_each(|(from, to)| {
126 let var_id_from = from.var_id.expect("Substitution not on variable");
127
128 vars.push(var_id_from);
129 replacements.push(to.bdd.clone());
130
131 let var_id_to = to.var_id.expect("Substitution not on variable");
132 vars.push(var_id_to);
133 replacements.push(from.bdd.clone());
134 });
135
136 let subs = Subst::new(vars, replacements);
137 let bdd = self
138 .bdd
139 .substitute(&subs)
140 .expect("OxiDD ran out of memory. Consider trying again with increased capacity");
141
142 OxiDD { bdd, var_id: None }
143 }
144}
145
146impl PartialEq for OxiDD {
147 fn eq(&self, other: &Self) -> bool {
150 let equiv = self.equiv(other);
151 let equiv_sat = equiv.satisfiable();
152 let n_equiv_unsat = !equiv.not().satisfiable();
153 equiv_sat && n_equiv_unsat
154 }
155}
156
157#[derive(Debug, Clone, PartialEq)]
161#[cfg_attr(feature = "config_deserialize", derive(Deserialize))]
162pub struct OxiddManagerConfig {
163 #[cfg_attr(
164 feature = "config_deserialize",
165 serde(default = "default_inner_node_capacity")
166 )]
167 pub inner_node_capacity: usize,
168 #[cfg_attr(
169 feature = "config_deserialize",
170 serde(default = "default_apply_cache_capacity")
171 )]
172 pub apply_cache_capacity: usize,
173 #[cfg_attr(feature = "config_deserialize", serde(default = "default_threads"))]
174 pub threads: u32,
175}
176
177fn default_inner_node_capacity() -> usize {
179 INNER_NODE_CAPACITY
180}
181
182fn default_apply_cache_capacity() -> usize {
184 APPLY_CACHE_CAPACITY
185}
186
187fn default_threads() -> u32 {
189 THREADS
190}
191
192impl Default for OxiddManagerConfig {
193 fn default() -> Self {
194 Self {
195 inner_node_capacity: default_inner_node_capacity(),
196 apply_cache_capacity: default_apply_cache_capacity(),
197 threads: default_threads(),
198 }
199 }
200}
201
202#[derive(Clone)]
210pub struct OxiddManager {
211 mgr: BDDManagerRef,
213 next_var_index: Rc<Mutex<u32>>,
215}
216
217impl PartialEq for OxiddManager {
218 fn eq(&self, other: &Self) -> bool {
220 self.mgr == other.mgr
221 }
222}
223
224impl OxiddManager {
225 pub fn new() -> Self {
235 Self {
236 mgr: oxidd::bdd::new_manager(INNER_NODE_CAPACITY, APPLY_CACHE_CAPACITY, THREADS),
237 next_var_index: Rc::new(Mutex::new(0)),
238 }
239 }
240
241 pub fn new_with_config(config: &OxiddManagerConfig) -> Self {
243 Self {
244 mgr: oxidd::bdd::new_manager(
245 config.inner_node_capacity,
246 config.apply_cache_capacity,
247 config.threads,
248 ),
249 next_var_index: Rc::new(Mutex::new(0)),
250 }
251 }
252}
253
254impl Default for OxiddManager {
255 fn default() -> Self {
256 Self::new()
257 }
258}
259
260impl Debug for OxiddManager {
261 fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
262 oxidd::bdd::print_stats();
263 Ok(())
264 }
265}
266
267impl BddManager for OxiddManager {
268 type DD = OxiDD;
269
270 fn new_var(&mut self) -> Self::DD {
271 let mut index = self.next_var_index.lock().unwrap();
272 let new_var_index = *index;
273 *index += 1;
274
275 let inner = self.mgr.with_manager_exclusive(|mgr| {
276 mgr.add_named_vars([new_var_index.to_string()])
277 .expect("OxiDD ran out of memory. Consider trying again with increased capacity");
278
279 BDDFunction::var(mgr, new_var_index)
280 .expect("OxiDD ran out of memory. Consider trying again with increased capacity")
281 });
282 OxiDD {
283 bdd: inner,
284 var_id: Some(new_var_index),
285 }
286 }
287
288 fn get_bdd_false(&self) -> Self::DD {
289 let inner = self.mgr.with_manager_exclusive(|mgr| BDDFunction::f(mgr));
290 OxiDD {
291 bdd: inner,
292 var_id: None,
293 }
294 }
295
296 fn get_bdd_true(&self) -> Self::DD {
297 let inner = self.mgr.with_manager_exclusive(|mgr| BDDFunction::t(mgr));
298 OxiDD {
299 bdd: inner,
300 var_id: None,
301 }
302 }
303
304 #[cfg(feature = "visualize")]
305 fn dump_dot<'a, VN, BD>(
306 &'a self,
307 file: std::fs::File,
308 _bdds: impl IntoIterator<Item = (&'a Self::DD, BD)>,
309 var_name: impl IntoIterator<Item = (&'a Self::DD, VN)>,
310 ) -> std::io::Result<()>
311 where
312 VN: std::fmt::Display + Clone,
313 BD: std::fmt::Display,
314 {
315 self.mgr.with_manager_shared(|manager| {
316 let var_name = var_name.into_iter().map(|(dd, vn)| (&dd.bdd, vn));
317
318 oxidd_dump::dot::dump_all(file, manager, var_name)
319 })
320 }
321}
322
323#[cfg(test)]
324mod test {
325 use crate::{
326 BddManager,
327 bdd_test_utils::{self, test_mgr_eq_and_clone},
328 };
329
330 use super::OxiddManager;
331
332 #[test]
333 fn test_mgr_eq() {
334 let mgr1 = OxiddManager::default();
335 let mgr2 = OxiddManager::default();
336
337 test_mgr_eq_and_clone(mgr1, mgr2);
338 }
339
340 #[test]
341 fn test_mgr_get() {
342 let mgr = OxiddManager::default();
343 bdd_test_utils::test_mgr_get(mgr)
344 }
345
346 #[test]
347 fn test_not() {
348 let mgr = OxiddManager::default();
349 bdd_test_utils::test_not(mgr)
350 }
351
352 #[test]
353 fn test_and() {
354 let mgr = OxiddManager::default();
355 bdd_test_utils::test_and(mgr)
356 }
357
358 #[test]
359 fn test_or() {
360 let mgr = OxiddManager::default();
361 bdd_test_utils::test_or(mgr)
362 }
363
364 #[test]
365 fn test_exists() {
366 let mgr = OxiddManager::default();
367 bdd_test_utils::test_exists(mgr)
368 }
369
370 #[test]
371 fn test_implies() {
372 let mgr = OxiddManager::default();
373 bdd_test_utils::test_implies(mgr)
374 }
375
376 #[test]
377 fn test_equiv() {
378 let mgr = OxiddManager::default();
379 bdd_test_utils::test_equiv(mgr)
380 }
381
382 #[test]
383 fn test_swap() {
384 let mgr = OxiddManager::default();
385 bdd_test_utils::test_swap(mgr)
386 }
387
388 #[test]
389 #[should_panic]
390 fn test_panic_on_bdd_of_mixed_mgrs() {
391 let mgr1 = OxiddManager::default();
392 let mgr2 = OxiddManager::default();
393 bdd_test_utils::panic_on_bdd_of_mixed_mgrs(mgr1, mgr2)
394 }
395
396 #[test]
397 fn print_funcs_work() {
398 let mut mgr = OxiddManager::new();
399 let v = mgr.new_var();
400
401 println!("Debug: {v:?}");
402 println!("Mgr Debug: {mgr:?}");
403 }
404}