1use std::hash::BuildHasher;
4
5use fixedbitset::FixedBitSet;
6
7use oxidd_core::{
8 function::{
9 BooleanFunction, BooleanFunctionQuant, BooleanOperator, EdgeOfFunc, Function,
10 FunctionSubst, INodeOfFunc,
11 },
12 util::{
13 AllocResult, Borrowed, EdgeDropGuard, EdgeVecDropGuard, OptBool, SatCountCache,
14 SatCountNumber,
15 },
16 ApplyCache, Edge, HasApplyCache, HasLevel, InnerNode, LevelNo, Manager, Node, NodeID, Tag,
17 VarNo,
18};
19use oxidd_derive::Function;
20use oxidd_dump::dot::DotStyle;
21
22use crate::complement_edge::{add_literal_to_cube, is_false};
23use crate::recursor::{Recursor, SequentialRecursor};
24use crate::stat;
25
26#[cfg(feature = "statistics")]
27use super::STAT_COUNTERS;
28use super::{
29 collect_cofactors, get_terminal, not, not_owned, reduce, BCDDOp, BCDDTerminal, EdgeTag,
30 NodesOrDone,
31};
32
33fn apply_bin<M, R: Recursor<M>, const OP: u8>(
43 manager: &M,
44 rec: R,
45 f: Borrowed<M::Edge>,
46 g: Borrowed<M::Edge>,
47) -> AllocResult<M::Edge>
48where
49 M: Manager<EdgeTag = EdgeTag, Terminal = BCDDTerminal> + HasApplyCache<M, BCDDOp>,
50 M::InnerNode: HasLevel,
51{
52 if rec.should_switch_to_sequential() {
53 return apply_bin::<M, _, OP>(manager, SequentialRecursor, f, g);
54 }
55 stat!(call OP);
56
57 let (op, f, fnode, g, gnode) = if OP == BCDDOp::And as u8 {
58 match super::terminal_and(manager, &f, &g) {
59 NodesOrDone::Nodes(fnode, gnode) if f < g => {
60 (BCDDOp::And, f.borrowed(), fnode, g.borrowed(), gnode)
61 }
62 NodesOrDone::Nodes(fnode, gnode) => {
66 (BCDDOp::And, g.borrowed(), gnode, f.borrowed(), fnode)
67 }
68 NodesOrDone::Done(h) => return Ok(h.into_edge()),
69 }
70 } else {
71 assert_eq!(OP, BCDDOp::Xor as u8);
72 match super::terminal_xor(manager, &f, &g) {
73 NodesOrDone::Nodes(fnode, gnode) if f < g => {
74 (BCDDOp::Xor, f.borrowed(), fnode, g.borrowed(), gnode)
75 }
76 NodesOrDone::Nodes(fnode, gnode) => {
77 (BCDDOp::Xor, g.borrowed(), gnode, f.borrowed(), fnode)
78 }
79 NodesOrDone::Done(h) => {
80 return Ok(h.into_edge());
81 }
82 }
83 };
84
85 stat!(cache_query OP);
87 if let Some(h) = manager
88 .apply_cache()
89 .get(manager, op, &[f.borrowed(), g.borrowed()])
90 {
91 stat!(cache_hit OP);
92 return Ok(h);
93 }
94
95 let flevel = fnode.level();
96 let glevel = gnode.level();
97 let level = std::cmp::min(flevel, glevel);
98
99 let (ft, fe) = if flevel == level {
101 collect_cofactors(f.tag(), fnode)
102 } else {
103 (f.borrowed(), f.borrowed())
104 };
105 let (gt, ge) = if glevel == level {
106 collect_cofactors(g.tag(), gnode)
107 } else {
108 (g.borrowed(), g.borrowed())
109 };
110
111 let (t, e) = rec.binary(apply_bin::<M, R, OP>, manager, (ft, gt), (fe, ge))?;
112
113 let h = reduce(manager, level, t.into_edge(), e.into_edge(), op)?;
114
115 manager
117 .apply_cache()
118 .add(manager, op, &[f, g], h.borrowed());
119
120 Ok(h)
121}
122
123#[inline(always)]
125fn apply_and<M, R: Recursor<M>>(
126 manager: &M,
127 rec: R,
128 f: Borrowed<M::Edge>,
129 g: Borrowed<M::Edge>,
130) -> AllocResult<M::Edge>
131where
132 M: Manager<EdgeTag = EdgeTag, Terminal = BCDDTerminal> + HasApplyCache<M, BCDDOp>,
133 M::InnerNode: HasLevel,
134{
135 apply_bin::<M, R, { BCDDOp::And as u8 }>(manager, rec, f, g)
136}
137
138fn apply_ite<M, R: Recursor<M>>(
140 manager: &M,
141 rec: R,
142 f: Borrowed<M::Edge>,
143 g: Borrowed<M::Edge>,
144 h: Borrowed<M::Edge>,
145) -> AllocResult<M::Edge>
146where
147 M: Manager<EdgeTag = EdgeTag, Terminal = BCDDTerminal> + HasApplyCache<M, BCDDOp>,
148 M::InnerNode: HasLevel,
149{
150 if rec.should_switch_to_sequential() {
151 return apply_ite(manager, SequentialRecursor, f, g, h);
152 }
153 stat!(call BCDDOp::Ite);
154
155 let gu = g.with_tag(EdgeTag::None); let hu = h.with_tag(EdgeTag::None);
158 if gu == hu {
159 return Ok(if g.tag() == h.tag() {
160 manager.clone_edge(&g)
161 } else {
162 not_owned(apply_bin::<M, R, { BCDDOp::Xor as u8 }>(
163 manager, rec, f, g,
164 )?) });
166 }
167 let fu = f.with_tag(EdgeTag::None);
168 if fu == gu {
169 return if f.tag() == g.tag() {
170 Ok(not_owned(apply_and(manager, rec, not(&f), not(&h))?)) } else {
172 apply_and(manager, rec, not(&f), h) };
174 }
175 if fu == hu {
176 return if f.tag() == h.tag() {
177 apply_and(manager, rec, f, g)
178 } else {
179 Ok(not_owned(apply_and(manager, rec, f, not(&g))?))
181 };
182 }
183 let fnode = match manager.get_node(&f) {
184 Node::Inner(n) => n,
185 Node::Terminal(_) => {
186 return Ok(manager.clone_edge(&*if f.tag() == EdgeTag::None { g } else { h }))
187 }
188 };
189 let (gnode, hnode) = match (manager.get_node(&g), manager.get_node(&h)) {
190 (Node::Inner(gn), Node::Inner(hn)) => (gn, hn),
191 (Node::Terminal(_), Node::Inner(_)) => {
192 return if g.tag() == EdgeTag::None {
193 Ok(not_owned(apply_and(manager, rec, not(&f), not(&h))?))
195 } else {
196 apply_and(manager, rec, not(&f), h) };
198 }
199 (_gnode, Node::Terminal(_)) => {
200 debug_assert!(_gnode.is_inner());
201 return if h.tag() == EdgeTag::None {
202 Ok(not_owned(apply_and(manager, rec, f, not(&g))?)) } else {
204 apply_and(manager, rec, f, g)
205 };
206 }
207 };
208
209 stat!(cache_query BCDDOp::Ite);
211 if let Some(res) = manager.apply_cache().get(
212 manager,
213 BCDDOp::Ite,
214 &[f.borrowed(), g.borrowed(), h.borrowed()],
215 ) {
216 stat!(cache_hit BCDDOp::Ite);
217 return Ok(res);
218 }
219
220 let flevel = fnode.level();
222 let glevel = gnode.level();
223 let hlevel = hnode.level();
224 let level = std::cmp::min(std::cmp::min(flevel, glevel), hlevel);
225
226 let (ft, fe) = if flevel == level {
228 collect_cofactors(f.tag(), fnode)
229 } else {
230 (f.borrowed(), f.borrowed())
231 };
232 let (gt, ge) = if glevel == level {
233 collect_cofactors(g.tag(), gnode)
234 } else {
235 (g.borrowed(), g.borrowed())
236 };
237 let (ht, he) = if hlevel == level {
238 collect_cofactors(h.tag(), hnode)
239 } else {
240 (h.borrowed(), h.borrowed())
241 };
242
243 let (t, e) = rec.ternary(apply_ite, manager, (ft, gt, ht), (fe, ge, he))?;
244 let res = reduce(manager, level, t.into_edge(), e.into_edge(), BCDDOp::Ite)?;
245
246 manager
247 .apply_cache()
248 .add(manager, BCDDOp::Ite, &[f, g, h], res.borrowed());
249
250 Ok(res)
251}
252
253fn substitute_prepare<'a, M>(
261 manager: &'a M,
262 pairs: impl Iterator<Item = (VarNo, Borrowed<'a, M::Edge>)>,
263) -> AllocResult<EdgeVecDropGuard<'a, M>>
264where
265 M: Manager<Terminal = BCDDTerminal, EdgeTag = EdgeTag>,
266 M::Edge: 'a,
267 M::InnerNode: HasLevel,
268{
269 let mut subst = Vec::with_capacity(manager.num_levels() as usize);
270 for (v, r) in pairs {
271 let level = manager.var_to_level(v) as usize;
272 if level >= subst.len() {
273 subst.resize_with(level + 1, || None);
274 }
275 debug_assert!(
276 subst[level].is_none(),
277 "Variable {v} occurs twice in the substitution, but a substitution \
278 should be a mapping from variables to replacement functions"
279 );
280 subst[level] = Some(r);
281 }
282
283 let mut res = EdgeVecDropGuard::new(manager, Vec::with_capacity(subst.len()));
284 for (level, e) in subst.into_iter().enumerate() {
285 use oxidd_core::LevelView;
286
287 res.push(if let Some(e) = e {
288 manager.clone_edge(&e)
289 } else {
290 let t = EdgeDropGuard::new(manager, get_terminal(manager, true));
291 let e = EdgeDropGuard::new(manager, get_terminal(manager, false));
292 manager
293 .level(level as LevelNo)
294 .get_or_insert(InnerNode::new(
295 level as LevelNo,
296 [t.into_edge(), e.into_edge()],
297 ))?
298 });
299 }
300
301 Ok(res)
302}
303
304fn substitute<M, R: Recursor<M>>(
305 manager: &M,
306 rec: R,
307 f: Borrowed<M::Edge>,
308 subst: &[M::Edge],
309 cache_id: u32,
310) -> AllocResult<M::Edge>
311where
312 M: Manager<EdgeTag = EdgeTag, Terminal = BCDDTerminal> + HasApplyCache<M, BCDDOp>,
313 M::InnerNode: HasLevel,
314{
315 if rec.should_switch_to_sequential() {
316 return substitute(manager, SequentialRecursor, f, subst, cache_id);
317 }
318 stat!(call BCDDOp::Substitute);
319
320 let Node::Inner(node) = manager.get_node(&f) else {
321 return Ok(manager.clone_edge(&f));
322 };
323 let level = node.level();
324 if level as usize >= subst.len() {
325 return Ok(manager.clone_edge(&f));
326 }
327
328 stat!(cache_query BCDDOp::Substitute);
330 if let Some(h) = manager.apply_cache().get_with_numeric(
331 manager,
332 BCDDOp::Substitute,
333 &[f.borrowed()],
334 &[cache_id],
335 ) {
336 stat!(cache_hit BCDDOp::Substitute);
337 return Ok(h);
338 }
339
340 let (t, e) = collect_cofactors(f.tag(), node);
341 let (t, e) = rec.subst(
342 substitute,
343 manager,
344 (t, subst, cache_id),
345 (e, subst, cache_id),
346 )?;
347 let res = apply_ite(
348 manager,
349 rec,
350 subst[level as usize].borrowed(),
351 t.borrowed(),
352 e.borrowed(),
353 )?;
354
355 manager.apply_cache().add_with_numeric(
357 manager,
358 BCDDOp::Substitute,
359 &[f.borrowed()],
360 &[cache_id],
361 res.borrowed(),
362 );
363
364 Ok(res)
365}
366
367enum RestrictInnerResult<'a, M: Manager> {
369 Done(M::Edge),
370 Rec {
371 vars: Borrowed<'a, M::Edge>,
372 f: Borrowed<'a, M::Edge>,
373 f_neg: bool,
374 fnode: &'a M::InnerNode,
375 },
376}
377
378#[inline]
385#[allow(clippy::too_many_arguments)]
386fn restrict_inner<'a, M>(
387 manager: &'a M,
388 f: Borrowed<'a, M::Edge>,
389 f_neg: bool,
390 fnode: &'a M::InnerNode,
391 flevel: LevelNo,
392 vars: Borrowed<'a, M::Edge>,
393 vars_neg: bool,
394 vnode: &'a M::InnerNode,
395) -> RestrictInnerResult<'a, M>
396where
397 M: Manager<EdgeTag = EdgeTag>,
398 M::InnerNode: HasLevel,
399{
400 debug_assert!(std::ptr::eq(manager.get_node(&f).unwrap_inner(), fnode));
401 debug_assert_eq!(fnode.level(), flevel);
402 debug_assert!(std::ptr::eq(manager.get_node(&vars).unwrap_inner(), vnode));
403
404 let vlevel = vnode.level();
405 if vlevel > flevel {
406 return RestrictInnerResult::Rec {
408 vars: vars.edge_with_tag(if vars_neg {
409 EdgeTag::Complemented
410 } else {
411 EdgeTag::None
412 }),
413 f,
414 f_neg,
415 fnode,
416 };
417 }
418
419 let (f, complement) = 'ret_f: {
420 let vt = vnode.child(0);
421 if vlevel < flevel {
422 if let Node::Inner(n) = manager.get_node(&vt) {
424 debug_assert!(
425 manager.get_node(&vnode.child(1)).is_any_terminal(),
426 "vars must be a conjunction of literals (but both children are non-terminals)"
427 );
428
429 debug_assert_eq!(
430 vnode.child(1).tag(),
431 if vars_neg {
432 EdgeTag::None
433 } else {
434 EdgeTag::Complemented
435 },
436 "vars must be a conjunction of literals (but is of shape ¬x ∨ {}φ)",
437 if vars_neg { "¬" } else { "" }
438 );
439 let vars_neg = vars_neg ^ (vt.tag() == EdgeTag::Complemented);
441 return restrict_inner(manager, f, f_neg, fnode, flevel, vt, vars_neg, n);
442 }
443 if vars_neg {
445 let ve = vnode.child(1);
447 if let Node::Inner(n) = manager.get_node(&ve) {
448 let vars_neg = ve.tag() != EdgeTag::Complemented;
450 return restrict_inner(manager, f, f_neg, fnode, flevel, ve, vars_neg, n);
451 }
452 } else {
454 debug_assert!(
455 manager.get_node(&vnode.child(1)).is_any_terminal(),
456 "vars must be a conjunction of literals (but is of shape x ∨ φ)"
457 );
458 }
460 break 'ret_f (f, f_neg);
462 }
463
464 debug_assert_eq!(vlevel, flevel);
465 let (f, vars, vars_neg, vnode) = if let Node::Inner(n) = manager.get_node(&vt) {
467 debug_assert!(
468 manager.get_node(&vnode.child(1)).is_any_terminal(),
469 "vars must be a conjunction of literals (but both children are non-terminals)"
470 );
471
472 debug_assert_eq!(
473 vnode.child(1).tag(),
474 if vars_neg {
475 EdgeTag::None
476 } else {
477 EdgeTag::Complemented
478 },
479 "vars must be a conjunction of literals (but is of shape ¬x ∨ {}φ)",
480 if vars_neg { "¬" } else { "" }
481 );
482 let vars_neg = vars_neg ^ (vt.tag() == EdgeTag::Complemented);
484 (fnode.child(0), vt, vars_neg, n)
485 } else {
486 if !vars_neg {
489 debug_assert!(
490 manager.get_node(&vnode.child(1)).is_any_terminal(),
491 "vars must be a conjunction of literals (but is of shape x ∨ φ)"
492 );
493
494 let f = fnode.child(0);
496 let f_neg = f_neg ^ (f.tag() == EdgeTag::Complemented);
497 break 'ret_f (f, f_neg);
498 }
499
500 let f = fnode.child(1);
502 let ve = vnode.child(1);
503 if let Node::Inner(n) = manager.get_node(&ve) {
504 let vars_neg = ve.tag() != EdgeTag::Complemented;
506 (f, ve, vars_neg, n)
507 } else {
508 let f_neg = f_neg ^ (f.tag() == EdgeTag::Complemented);
510 break 'ret_f (f, f_neg);
511 }
512 };
513
514 let f_neg = f_neg ^ (f.tag() == EdgeTag::Complemented);
515 if let Node::Inner(fnode) = manager.get_node(&f) {
516 let flevel = fnode.level();
517 return restrict_inner(manager, f, f_neg, fnode, flevel, vars, vars_neg, vnode);
518 }
519 (f, f_neg)
520 };
521
522 RestrictInnerResult::Done(manager.clone_edge(&f).with_tag_owned(if complement {
523 EdgeTag::Complemented
524 } else {
525 EdgeTag::None
526 }))
527}
528
529fn restrict<M, R: Recursor<M>>(
530 manager: &M,
531 rec: R,
532 f: Borrowed<M::Edge>,
533 vars: Borrowed<M::Edge>,
534) -> AllocResult<M::Edge>
535where
536 M: Manager<Terminal = BCDDTerminal, EdgeTag = EdgeTag> + HasApplyCache<M, BCDDOp>,
537 M::InnerNode: HasLevel,
538{
539 if rec.should_switch_to_sequential() {
540 return restrict(manager, SequentialRecursor, f, vars);
541 }
542 stat!(call BCDDOp::Restrict);
543
544 let (Node::Inner(fnode), Node::Inner(vnode)) = (manager.get_node(&f), manager.get_node(&vars))
545 else {
546 return Ok(manager.clone_edge(&f));
547 };
548
549 let inner_res = {
550 let f_neg = f.tag() == EdgeTag::Complemented;
551 let flevel = fnode.level();
552 let vars_neg = vars.tag() == EdgeTag::Complemented;
553 restrict_inner(manager, f, f_neg, fnode, flevel, vars, vars_neg, vnode)
554 };
555 match inner_res {
556 RestrictInnerResult::Done(result) => Ok(result),
557 RestrictInnerResult::Rec {
558 vars,
559 f,
560 f_neg,
561 fnode,
562 } => {
563 let f_untagged = f.with_tag(EdgeTag::None);
565 let f_tag = if f_neg {
566 EdgeTag::Complemented
567 } else {
568 EdgeTag::None
569 };
570
571 stat!(cache_query BCDDOp::Restrict);
573 if let Some(result) = manager.apply_cache().get(
574 manager,
575 BCDDOp::Restrict,
576 &[f_untagged.borrowed(), vars.borrowed()],
577 ) {
578 stat!(cache_hit BCDDOp::Restrict);
579 let result_tag = result.tag();
580 return Ok(result.with_tag_owned(result_tag ^ f_tag));
581 }
582
583 let (t, e) = rec.binary(
584 restrict,
585 manager,
586 (fnode.child(0), vars.borrowed()),
587 (fnode.child(1), vars.borrowed()),
588 )?;
589
590 let result = reduce(
591 manager,
592 fnode.level(),
593 t.into_edge(),
594 e.into_edge(),
595 BCDDOp::Restrict,
596 )?;
597
598 manager.apply_cache().add(
599 manager,
600 BCDDOp::Restrict,
601 &[f_untagged, vars],
602 result.borrowed(),
603 );
604
605 let result_tag = result.tag();
606 Ok(result.with_tag_owned(result_tag ^ f_tag))
607 }
608 }
609}
610
611fn quant<M, R: Recursor<M>, const Q: u8>(
616 manager: &M,
617 rec: R,
618 f: Borrowed<M::Edge>,
619 vars: Borrowed<M::Edge>,
620) -> AllocResult<M::Edge>
621where
622 M: Manager<Terminal = BCDDTerminal, EdgeTag = EdgeTag> + HasApplyCache<M, BCDDOp>,
623 M::InnerNode: HasLevel,
624{
625 if rec.should_switch_to_sequential() {
626 return quant::<M, _, Q>(manager, SequentialRecursor, f, vars);
627 }
628 let operator = match () {
629 _ if Q == BCDDOp::Forall as u8 => BCDDOp::Forall,
630 _ if Q == BCDDOp::Exists as u8 => BCDDOp::Exists,
631 _ if Q == BCDDOp::Unique as u8 => BCDDOp::Unique,
632 _ => unreachable!("invalid quantifier"),
633 };
634
635 stat!(call operator);
636 let fnode = match manager.get_node(&f) {
638 Node::Inner(n) => n,
639 Node::Terminal(_) => {
640 return Ok(
641 if operator != BCDDOp::Unique || manager.get_node(&vars).is_any_terminal() {
642 manager.clone_edge(&f)
643 } else {
644 get_terminal(manager, false)
645 },
646 );
647 }
648 };
649 let flevel = fnode.level();
650
651 let vars = if operator != BCDDOp::Unique {
652 crate::set_pop(manager, vars, flevel)
656 } else {
657 vars
661 };
662 let vnode = match manager.get_node(&vars) {
663 Node::Inner(n) => n,
664 Node::Terminal(_) => return Ok(manager.clone_edge(&f)),
665 };
666 let vlevel = vnode.level();
667 if operator == BCDDOp::Unique && vlevel < flevel {
668 return Ok(get_terminal(manager, false));
670 }
671 debug_assert!(flevel <= vlevel);
672 let vars = vars.borrowed();
673
674 stat!(cache_query operator);
676 if let Some(res) =
677 manager
678 .apply_cache()
679 .get(manager, operator, &[f.borrowed(), vars.borrowed()])
680 {
681 stat!(cache_hit operator);
682 return Ok(res);
683 }
684
685 let (ft, fe) = collect_cofactors(f.tag(), fnode);
686 let vt = if vlevel == flevel {
687 vnode.child(0)
688 } else {
689 vars.borrowed()
690 };
691 let (t, e) = rec.binary(
692 quant::<M, R, Q>,
693 manager,
694 (ft, vt.borrowed()),
695 (fe, vt.borrowed()),
696 )?;
697
698 let res = if flevel == vlevel {
699 match operator {
700 BCDDOp::Forall => apply_and(manager, rec, t.borrowed(), e.borrowed())?,
701 BCDDOp::Exists => not_owned(apply_and(manager, rec, not(&t), not(&e))?),
702 BCDDOp::Unique => {
703 apply_bin::<M, R, { BCDDOp::Xor as u8 }>(manager, rec, t.borrowed(), e.borrowed())?
704 }
705 _ => unreachable!(),
706 }
707 } else {
708 reduce(manager, flevel, t.into_edge(), e.into_edge(), operator)?
709 };
710
711 manager
712 .apply_cache()
713 .add(manager, operator, &[f, vars], res.borrowed());
714
715 Ok(res)
716}
717
718fn apply_quant<'a, M, R: Recursor<M>, const Q: u8, const OP: u8>(
731 manager: &'a M,
732 rec: R,
733 f: Borrowed<M::Edge>,
734 g: Borrowed<M::Edge>,
735 vars: Borrowed<M::Edge>,
736) -> AllocResult<M::Edge>
737where
738 M: Manager<Terminal = BCDDTerminal, EdgeTag = EdgeTag> + HasApplyCache<M, BCDDOp>,
739 M::InnerNode: HasLevel,
740{
741 if rec.should_switch_to_sequential() {
742 return apply_quant::<M, _, Q, OP>(manager, SequentialRecursor, f, g, vars);
743 }
744
745 let operator = const { BCDDOp::from_apply_quant(Q, OP) };
746
747 stat!(call operator);
748 let (f, fnode, g, gnode) = if OP == BCDDOp::And as u8 || OP == BCDDOp::UniqueNand as u8 {
750 match super::terminal_and(manager, &f, &g) {
751 NodesOrDone::Nodes(fnode, gnode) if f < g => (f.borrowed(), fnode, g.borrowed(), gnode),
752 NodesOrDone::Nodes(fnode, gnode) => (g.borrowed(), gnode, f.borrowed(), fnode),
756 NodesOrDone::Done(h) if OP == BCDDOp::UniqueNand as u8 => {
757 return quant::<M, R, Q>(manager, rec, not(&h), vars)
758 }
759 NodesOrDone::Done(h) => return quant::<M, R, Q>(manager, rec, h.borrowed(), vars),
760 }
761 } else {
762 assert_eq!(OP, BCDDOp::Xor as u8);
763 match super::terminal_xor(manager, &f, &g) {
764 NodesOrDone::Nodes(fnode, gnode) if f < g => (f.borrowed(), fnode, g.borrowed(), gnode),
765 NodesOrDone::Nodes(fnode, gnode) => (g.borrowed(), gnode, f.borrowed(), fnode),
766 NodesOrDone::Done(h) => return quant::<M, R, Q>(manager, rec, h.borrowed(), vars),
767 }
768 };
769
770 let flevel = fnode.level();
771 let glevel = gnode.level();
772 let min_level = std::cmp::min(fnode.level(), gnode.level());
773
774 let vars = if Q != BCDDOp::Unique as u8 {
775 crate::set_pop(manager, vars, min_level)
779 } else {
780 vars
784 };
785
786 let vnode = match manager.get_node(&vars) {
787 Node::Inner(n) => n,
788 Node::Terminal(_) if OP == BCDDOp::UniqueNand as u8 => {
790 return Ok(not_owned(apply_and(manager, rec, f, g)?))
791 }
792 Node::Terminal(_) => return apply_bin::<M, R, OP>(manager, rec, f, g),
793 };
794
795 let vlevel = vnode.level();
796 if vlevel < min_level && Q == BCDDOp::Unique as u8 {
797 return Ok(get_terminal(manager, false));
800 }
801
802 if min_level > vlevel {
803 if OP == BCDDOp::UniqueNand as u8 {
805 return Ok(not_owned(apply_and(manager, rec, f, g)?));
806 }
807 return apply_bin::<M, R, OP>(manager, rec, f, g);
808 }
809
810 stat!(cache_query operator);
812 if let Some(res) = manager.apply_cache().get(
813 manager,
814 operator,
815 &[f.borrowed(), g.borrowed(), vars.borrowed()],
816 ) {
817 stat!(cache_hit operator);
818 return Ok(res);
819 }
820
821 let vt = if vlevel == min_level {
822 vnode.child(0)
823 } else {
824 vars.borrowed()
825 };
826
827 let (ft, fe) = if flevel <= glevel {
828 collect_cofactors(f.tag(), fnode)
829 } else {
830 (f.borrowed(), f.borrowed())
831 };
832
833 let (gt, ge) = if flevel >= glevel {
834 collect_cofactors(g.tag(), gnode)
835 } else {
836 (g.borrowed(), g.borrowed())
837 };
838
839 let (t, e) = rec.ternary(
840 apply_quant::<M, R, Q, OP>,
841 manager,
842 (ft, gt, vt.borrowed()),
843 (fe, ge, vt.borrowed()),
844 )?;
845
846 let res = if min_level == vlevel {
847 if Q == BCDDOp::Forall as u8 {
848 apply_and(manager, rec, t.borrowed(), e.borrowed())?
849 } else if Q == BCDDOp::Exists as u8 {
850 not_owned(apply_and(manager, rec, not(&t), not(&e))?)
851 } else if Q == BCDDOp::Unique as u8 {
852 apply_bin::<M, R, { BCDDOp::Xor as u8 }>(manager, rec, t.borrowed(), e.borrowed())?
853 } else {
854 unreachable!()
855 }
856 } else {
857 reduce(manager, min_level, t.into_edge(), e.into_edge(), operator)?
858 };
859
860 manager
861 .apply_cache()
862 .add(manager, operator, &[f, g, vars], res.borrowed());
863
864 Ok(res)
865}
866
867fn apply_quant_dispatch<'a, M, R: Recursor<M>, const Q: u8, const QN: u8>(
874 manager: &'a M,
875 rec: R,
876 op: BooleanOperator,
877 f: Borrowed<M::Edge>,
878 g: Borrowed<M::Edge>,
879 vars: Borrowed<M::Edge>,
880) -> AllocResult<M::Edge>
881where
882 M: Manager<Terminal = BCDDTerminal, EdgeTag = EdgeTag> + HasApplyCache<M, BCDDOp>,
883 M::InnerNode: HasLevel,
884{
885 use BooleanOperator::*;
886 const OA: u8 = BCDDOp::And as u8;
887 const OX: u8 = BCDDOp::Xor as u8;
888
889 const {
890 assert!(
891 (Q == BCDDOp::Forall as u8 && QN == BCDDOp::Exists as u8)
892 || (Q == BCDDOp::Exists as u8 && QN == BCDDOp::Forall as u8)
893 );
894 }
895
896 match op {
897 And => apply_quant::<M, R, Q, OA>(manager, rec, f, g, vars),
898 Or => {
899 let tmp = apply_quant::<M, R, QN, OA>(manager, rec, not(&f), not(&g), vars)?;
900 Ok(not_owned(tmp))
901 }
902 Xor => apply_quant::<M, R, Q, OX>(manager, rec, f, g, vars),
903 Equiv => {
904 let tmp = apply_quant::<M, R, QN, OX>(manager, rec, f, g, vars)?;
905 Ok(not_owned(tmp))
906 }
907 Nand => {
908 let tmp = apply_quant::<M, R, QN, OA>(manager, rec, f, g, vars)?;
909 Ok(not_owned(tmp))
910 }
911 Nor => apply_quant::<M, R, Q, OA>(manager, rec, not(&f), not(&g), vars),
912 Imp => {
913 let tmp = apply_quant::<M, R, QN, OA>(manager, rec, f, not(&g), vars)?;
914 Ok(not_owned(tmp))
915 }
916 ImpStrict => apply_quant::<M, R, Q, OA>(manager, rec, not(&f), g, vars),
917 }
918}
919
920fn apply_quant_unique_dispatch<'a, M, R: Recursor<M>>(
925 manager: &'a M,
926 rec: R,
927 op: BooleanOperator,
928 f: Borrowed<M::Edge>,
929 g: Borrowed<M::Edge>,
930 vars: Borrowed<M::Edge>,
931) -> AllocResult<M::Edge>
932where
933 M: Manager<Terminal = BCDDTerminal, EdgeTag = EdgeTag> + HasApplyCache<M, BCDDOp>,
934 M::InnerNode: HasLevel,
935{
936 use BooleanOperator::*;
937 const Q: u8 = BCDDOp::Unique as u8;
938 const OA: u8 = BCDDOp::And as u8;
939 const OX: u8 = BCDDOp::Xor as u8;
940 const ONA: u8 = BCDDOp::UniqueNand as u8;
941
942 match op {
943 And => apply_quant::<M, R, Q, OA>(manager, rec, f, g, vars),
944 Or => apply_quant::<M, R, Q, ONA>(manager, rec, not(&f), not(&g), vars),
945 Xor => apply_quant::<M, R, Q, OX>(manager, rec, f, g, vars),
946 Equiv => apply_quant::<M, R, Q, OX>(manager, rec, not(&f), g, vars),
947 Nand => apply_quant::<M, R, Q, ONA>(manager, rec, f, g, vars),
948 Nor => apply_quant::<M, R, Q, OA>(manager, rec, not(&f), not(&g), vars),
949 Imp => apply_quant::<M, R, Q, ONA>(manager, rec, f, not(&g), vars),
950 ImpStrict => apply_quant::<M, R, Q, OA>(manager, rec, not(&f), g, vars),
951 }
952}
953
954trait HasBCDDOpApplyCache<M: Manager>: HasApplyCache<M, BCDDOp> {}
958impl<M: Manager + HasApplyCache<M, BCDDOp>> HasBCDDOpApplyCache<M> for M {}
959
960#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Function, Debug)]
962#[repr_id = "BCDD"]
963#[repr(transparent)]
964pub struct BCDDFunction<F: Function>(F);
965
966impl<F: Function> From<F> for BCDDFunction<F> {
967 #[inline(always)]
968 fn from(value: F) -> Self {
969 BCDDFunction(value)
970 }
971}
972
973impl<F: Function> BCDDFunction<F> {
974 #[inline(always)]
976 pub fn into_inner(self) -> F {
977 self.0
978 }
979}
980
981impl<F: Function> FunctionSubst for BCDDFunction<F>
982where
983 for<'id> F::Manager<'id>:
984 Manager<Terminal = BCDDTerminal, EdgeTag = EdgeTag> + HasBCDDOpApplyCache<F::Manager<'id>>,
985 for<'id> INodeOfFunc<'id, F>: HasLevel,
986{
987 fn substitute_edge<'id, 'a>(
988 manager: &'a Self::Manager<'id>,
989 edge: &'a EdgeOfFunc<'id, Self>,
990 substitution: impl oxidd_core::util::Substitution<
991 Replacement = Borrowed<'a, EdgeOfFunc<'id, Self>>,
992 >,
993 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
994 let rec = SequentialRecursor;
995 let subst = substitute_prepare(manager, substitution.pairs())?;
996 substitute(manager, rec, edge.borrowed(), &subst, substitution.id())
997 }
998}
999
1000impl<F: Function> BooleanFunction for BCDDFunction<F>
1001where
1002 for<'id> F::Manager<'id>:
1003 Manager<Terminal = BCDDTerminal, EdgeTag = EdgeTag> + HasBCDDOpApplyCache<F::Manager<'id>>,
1004 for<'id> INodeOfFunc<'id, F>: HasLevel,
1005{
1006 fn var_edge<'id>(
1007 manager: &Self::Manager<'id>,
1008 var: VarNo,
1009 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1010 let t = get_terminal(manager, true);
1011 let e = get_terminal(manager, false);
1012 let level = manager.var_to_level(var);
1013 oxidd_core::LevelView::get_or_insert(
1014 &mut manager.level(level),
1015 InnerNode::new(level, [t, e]),
1016 )
1017 }
1018
1019 #[inline]
1020 fn f_edge<'id>(manager: &Self::Manager<'id>) -> EdgeOfFunc<'id, Self> {
1021 get_terminal(manager, false)
1022 }
1023 #[inline]
1024 fn t_edge<'id>(manager: &Self::Manager<'id>) -> EdgeOfFunc<'id, Self> {
1025 get_terminal(manager, true)
1026 }
1027
1028 #[inline]
1029 fn not_edge<'id>(
1030 manager: &Self::Manager<'id>,
1031 edge: &EdgeOfFunc<'id, Self>,
1032 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1033 Ok(not_owned(manager.clone_edge(edge)))
1034 }
1035 #[inline]
1036 fn not_edge_owned<'id>(
1037 _manager: &Self::Manager<'id>,
1038 edge: EdgeOfFunc<'id, Self>,
1039 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1040 Ok(not_owned(edge))
1041 }
1042
1043 #[inline]
1044 fn and_edge<'id>(
1045 manager: &Self::Manager<'id>,
1046 lhs: &EdgeOfFunc<'id, Self>,
1047 rhs: &EdgeOfFunc<'id, Self>,
1048 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1049 let rec = SequentialRecursor;
1050 apply_and(manager, rec, lhs.borrowed(), rhs.borrowed())
1051 }
1052 #[inline]
1053 fn or_edge<'id>(
1054 manager: &Self::Manager<'id>,
1055 lhs: &EdgeOfFunc<'id, Self>,
1056 rhs: &EdgeOfFunc<'id, Self>,
1057 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1058 Ok(not_owned(Self::nor_edge(manager, lhs, rhs)?))
1059 }
1060 #[inline]
1061 fn nand_edge<'id>(
1062 manager: &Self::Manager<'id>,
1063 lhs: &EdgeOfFunc<'id, Self>,
1064 rhs: &EdgeOfFunc<'id, Self>,
1065 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1066 Ok(not_owned(Self::and_edge(manager, lhs, rhs)?))
1067 }
1068 #[inline]
1069 fn nor_edge<'id>(
1070 manager: &Self::Manager<'id>,
1071 lhs: &EdgeOfFunc<'id, Self>,
1072 rhs: &EdgeOfFunc<'id, Self>,
1073 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1074 let rec = SequentialRecursor;
1075 apply_and(manager, rec, not(lhs), not(rhs))
1076 }
1077 #[inline]
1078 fn xor_edge<'id>(
1079 manager: &Self::Manager<'id>,
1080 lhs: &EdgeOfFunc<'id, Self>,
1081 rhs: &EdgeOfFunc<'id, Self>,
1082 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1083 let rec = SequentialRecursor;
1084 apply_bin::<_, _, { BCDDOp::Xor as u8 }>(manager, rec, lhs.borrowed(), rhs.borrowed())
1085 }
1086 #[inline]
1087 fn equiv_edge<'id>(
1088 manager: &Self::Manager<'id>,
1089 lhs: &EdgeOfFunc<'id, Self>,
1090 rhs: &EdgeOfFunc<'id, Self>,
1091 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1092 Ok(not_owned(Self::xor_edge(manager, lhs, rhs)?))
1093 }
1094 #[inline]
1095 fn imp_edge<'id>(
1096 manager: &Self::Manager<'id>,
1097 lhs: &EdgeOfFunc<'id, Self>,
1098 rhs: &EdgeOfFunc<'id, Self>,
1099 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1100 let rec = SequentialRecursor;
1101 Ok(not_owned(apply_and(
1102 manager,
1103 rec,
1104 lhs.borrowed(),
1105 not(rhs),
1106 )?))
1107 }
1108 #[inline]
1109 fn imp_strict_edge<'id>(
1110 manager: &Self::Manager<'id>,
1111 lhs: &EdgeOfFunc<'id, Self>,
1112 rhs: &EdgeOfFunc<'id, Self>,
1113 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1114 let rec = SequentialRecursor;
1115 apply_and(manager, rec, not(lhs), rhs.borrowed())
1116 }
1117
1118 #[inline]
1119 fn ite_edge<'id>(
1120 manager: &Self::Manager<'id>,
1121 if_edge: &EdgeOfFunc<'id, Self>,
1122 then_edge: &EdgeOfFunc<'id, Self>,
1123 else_edge: &EdgeOfFunc<'id, Self>,
1124 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1125 apply_ite(
1126 manager,
1127 SequentialRecursor,
1128 if_edge.borrowed(),
1129 then_edge.borrowed(),
1130 else_edge.borrowed(),
1131 )
1132 }
1133
1134 #[inline]
1135 fn sat_count_edge<'id, N: SatCountNumber, S: BuildHasher>(
1136 manager: &Self::Manager<'id>,
1137 edge: &EdgeOfFunc<'id, Self>,
1138 vars: LevelNo,
1139 cache: &mut SatCountCache<N, S>,
1140 ) -> N {
1141 fn inner<M, N: SatCountNumber, S: BuildHasher>(
1142 manager: &M,
1143 e: Borrowed<M::Edge>,
1144 terminal_val: &N,
1145 cache: &mut SatCountCache<N, S>,
1146 ) -> N
1147 where
1148 M: Manager<EdgeTag = EdgeTag, Terminal = BCDDTerminal>,
1149 {
1150 let node = match manager.get_node(&e) {
1151 Node::Inner(node) => node,
1152 Node::Terminal(_) => return terminal_val.clone(),
1153 };
1154
1155 let node_id = e.node_id();
1157 if let Some(n) = cache.map.get(&node_id) {
1158 return n.clone();
1159 }
1160
1161 let mut iter = node.children().map(|c| {
1163 let n = inner(manager, c.borrowed(), terminal_val, cache);
1164 match c.tag() {
1165 EdgeTag::None => n,
1166 EdgeTag::Complemented => {
1167 let mut res = terminal_val.clone();
1168 res -= &n;
1169 res
1170 }
1171 }
1172 });
1173
1174 let mut n = iter.next().unwrap();
1175 n += &iter.next().unwrap();
1176 debug_assert!(iter.next().is_none());
1177 n >>= 1u32;
1178 cache.map.insert(node_id, n.clone());
1179 n
1180 }
1181
1182 fn inner_floating<M, N, S>(
1185 manager: &M,
1186 e: Borrowed<M::Edge>,
1187 terminal_val: &N,
1188 cache: &mut SatCountCache<N, S>,
1189 ) -> N
1190 where
1191 M: Manager<EdgeTag = EdgeTag, Terminal = BCDDTerminal>,
1192 N: SatCountNumber,
1193 S: BuildHasher,
1194 {
1195 let tag = e.tag();
1196 let node = match manager.get_node(&e) {
1197 Node::Inner(node) => node,
1198 Node::Terminal(_) if tag == EdgeTag::None => return terminal_val.clone(),
1199 Node::Terminal(_) => return N::from(0u32),
1200 };
1201 let node_id = e.node_id() | ((tag as NodeID) << (NodeID::BITS - 1));
1203 if let Some(n) = cache.map.get(&node_id) {
1204 return n.clone();
1205 }
1206 let (e0, e1) = collect_cofactors(tag, node);
1207 let mut n = inner_floating(manager, e0, terminal_val, cache);
1208 n += &inner_floating(manager, e1, terminal_val, cache);
1209 n >>= 1u32;
1210 cache.map.insert(node_id, n.clone());
1211 n
1212 }
1213
1214 cache.clear_if_invalid(manager, vars);
1215
1216 if N::FLOATING_POINT {
1217 let mut terminal_val = N::from(1u32);
1218 let scale_exp = (-N::MIN_EXP) as u32;
1219 terminal_val <<= if vars >= scale_exp {
1220 vars - scale_exp
1222 } else {
1223 vars
1224 };
1225 let mut res = inner_floating(manager, edge.borrowed(), &terminal_val, cache);
1226 if vars >= scale_exp {
1227 res <<= scale_exp; }
1229 res
1230 } else {
1231 let mut terminal_val = N::from(1u32);
1232 terminal_val <<= vars;
1233 let n = inner(manager, edge.borrowed(), &terminal_val, cache);
1234 match edge.tag() {
1235 EdgeTag::None => n,
1236 EdgeTag::Complemented => {
1237 terminal_val -= &n;
1238 terminal_val
1239 }
1240 }
1241 }
1242 }
1243
1244 #[inline]
1245 fn pick_cube_edge<'id>(
1246 manager: &Self::Manager<'id>,
1247 edge: &EdgeOfFunc<'id, Self>,
1248 choice: impl FnMut(&Self::Manager<'id>, &EdgeOfFunc<'id, Self>, LevelNo) -> bool,
1249 ) -> Option<Vec<OptBool>> {
1250 #[inline] fn inner<M: Manager<EdgeTag = EdgeTag>>(
1252 manager: &M,
1253 edge: Borrowed<M::Edge>,
1254 cube: &mut [OptBool],
1255 mut choice: impl FnMut(&M, &M::Edge, LevelNo) -> bool,
1256 ) where
1257 M::InnerNode: HasLevel,
1258 {
1259 let Node::Inner(node) = manager.get_node(&edge) else {
1260 return;
1261 };
1262 let tag = edge.tag();
1263 let level = node.level();
1264 let (t, e) = collect_cofactors(tag, node);
1265 let c = if is_false(manager, &t) {
1266 false
1267 } else if is_false(manager, &e) {
1268 true
1269 } else {
1270 choice(manager, &edge, level)
1271 };
1272 cube[manager.level_to_var(level) as usize] = OptBool::from(c);
1273 inner(manager, if c { t } else { e }, cube, choice);
1274 }
1275
1276 if manager.get_node(edge).is_any_terminal() {
1277 return match edge.tag() {
1278 EdgeTag::None => Some(vec![OptBool::None; manager.num_levels() as usize]),
1279 EdgeTag::Complemented => None,
1280 };
1281 }
1282
1283 let mut cube = vec![OptBool::None; manager.num_levels() as usize];
1284 inner(manager, edge.borrowed(), &mut cube, choice);
1285 Some(cube)
1286 }
1287
1288 #[inline]
1289 fn pick_cube_dd_edge<'id>(
1290 manager: &Self::Manager<'id>,
1291 edge: &EdgeOfFunc<'id, Self>,
1292 choice: impl FnMut(&Self::Manager<'id>, &EdgeOfFunc<'id, Self>, LevelNo) -> bool,
1293 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1294 fn inner<M: Manager<EdgeTag = EdgeTag, Terminal = BCDDTerminal>>(
1295 manager: &M,
1296 edge: Borrowed<M::Edge>,
1297 mut choice: impl FnMut(&M, &M::Edge, LevelNo) -> bool,
1298 ) -> AllocResult<M::Edge>
1299 where
1300 M::InnerNode: HasLevel,
1301 {
1302 let Node::Inner(node) = manager.get_node(&edge) else {
1303 return Ok(manager.clone_edge(&edge));
1304 };
1305
1306 let (t, e) = collect_cofactors(edge.tag(), node);
1307 let level = node.level();
1308 let c = if is_false(manager, &t) {
1309 false
1310 } else if is_false(manager, &e) {
1311 true
1312 } else {
1313 choice(manager, &edge, level)
1314 };
1315
1316 let sub = inner(manager, if c { t } else { e }, choice)?;
1317 add_literal_to_cube(manager, sub, level, c)
1318 }
1319
1320 inner(manager, edge.borrowed(), choice)
1321 }
1322
1323 fn pick_cube_dd_set_edge<'id>(
1324 manager: &Self::Manager<'id>,
1325 edge: &EdgeOfFunc<'id, Self>,
1326 literal_set: &EdgeOfFunc<'id, Self>,
1327 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1328 fn inner<M: Manager<EdgeTag = EdgeTag, Terminal = BCDDTerminal>>(
1329 manager: &M,
1330 edge: Borrowed<M::Edge>,
1331 literal_set: Borrowed<M::Edge>,
1332 ) -> AllocResult<M::Edge>
1333 where
1334 M::InnerNode: HasLevel,
1335 {
1336 let Node::Inner(node) = manager.get_node(&edge) else {
1337 return Ok(manager.clone_edge(&edge));
1338 };
1339 let level = node.level();
1340
1341 let literal_set = crate::set_pop(manager, literal_set, level);
1342 let (literal_set, c) = match manager.get_node(&literal_set) {
1343 Node::Inner(node) if node.level() == level => {
1344 let (t, e) = collect_cofactors(literal_set.tag(), node);
1345 if is_false(manager, &e) {
1346 (e, true)
1347 } else {
1348 (t, false)
1349 }
1350 }
1351 _ => (literal_set, false),
1352 };
1353
1354 let (t, e) = collect_cofactors(edge.tag(), node);
1355 let c = if is_false(manager, &t) {
1356 false
1357 } else if is_false(manager, &e) {
1358 true
1359 } else {
1360 c
1361 };
1362
1363 let sub = inner(manager, if c { t } else { e }, literal_set)?;
1364 add_literal_to_cube(manager, sub, level, c)
1365 }
1366
1367 inner(manager, edge.borrowed(), literal_set.borrowed())
1368 }
1369
1370 #[inline]
1371 fn eval_edge<'id>(
1372 manager: &Self::Manager<'id>,
1373 edge: &EdgeOfFunc<'id, Self>,
1374 args: impl IntoIterator<Item = (VarNo, bool)>,
1375 ) -> bool {
1376 let mut choices = FixedBitSet::with_capacity(manager.num_levels() as usize);
1378 for (var, val) in args {
1379 choices.set(manager.var_to_level(var) as usize, !val);
1381 }
1382
1383 #[inline] fn inner<M>(
1385 manager: &M,
1386 edge: Borrowed<M::Edge>,
1387 complement: bool,
1388 choices: &FixedBitSet,
1389 ) -> bool
1390 where
1391 M: Manager<EdgeTag = EdgeTag>,
1392 M::InnerNode: HasLevel,
1393 {
1394 let complement = complement ^ (edge.tag() == EdgeTag::Complemented);
1395 match manager.get_node(&edge) {
1396 Node::Inner(node) => {
1397 let edge = node.child(choices.contains(node.level() as usize) as usize);
1398 inner(manager, edge, complement, choices)
1399 }
1400 Node::Terminal(_) => !complement,
1401 }
1402 }
1403
1404 inner(manager, edge.borrowed(), false, &choices)
1405 }
1406}
1407
1408impl<F: Function> BooleanFunctionQuant for BCDDFunction<F>
1409where
1410 for<'id> F::Manager<'id>:
1411 Manager<Terminal = BCDDTerminal, EdgeTag = EdgeTag> + HasBCDDOpApplyCache<F::Manager<'id>>,
1412 for<'id> INodeOfFunc<'id, F>: HasLevel,
1413{
1414 #[inline]
1415 fn restrict_edge<'id>(
1416 manager: &Self::Manager<'id>,
1417 root: &EdgeOfFunc<'id, Self>,
1418 vars: &EdgeOfFunc<'id, Self>,
1419 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1420 let rec = SequentialRecursor;
1421 restrict(manager, rec, root.borrowed(), vars.borrowed())
1422 }
1423
1424 #[inline]
1425 fn forall_edge<'id>(
1426 manager: &Self::Manager<'id>,
1427 root: &EdgeOfFunc<'id, Self>,
1428 vars: &EdgeOfFunc<'id, Self>,
1429 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1430 let rec = SequentialRecursor;
1431 quant::<_, _, { BCDDOp::Forall as u8 }>(manager, rec, root.borrowed(), vars.borrowed())
1432 }
1433 #[inline]
1434 fn exists_edge<'id>(
1435 manager: &Self::Manager<'id>,
1436 root: &EdgeOfFunc<'id, Self>,
1437 vars: &EdgeOfFunc<'id, Self>,
1438 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1439 let rec = SequentialRecursor;
1440 quant::<_, _, { BCDDOp::Exists as u8 }>(manager, rec, root.borrowed(), vars.borrowed())
1441 }
1442 #[inline]
1443 fn unique_edge<'id>(
1444 manager: &Self::Manager<'id>,
1445 root: &EdgeOfFunc<'id, Self>,
1446 vars: &EdgeOfFunc<'id, Self>,
1447 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1448 let rec = SequentialRecursor;
1449 quant::<_, _, { BCDDOp::Unique as u8 }>(manager, rec, root.borrowed(), vars.borrowed())
1450 }
1451
1452 #[inline]
1453 fn apply_forall_edge<'id>(
1454 manager: &Self::Manager<'id>,
1455 op: BooleanOperator,
1456 lhs: &EdgeOfFunc<'id, Self>,
1457 rhs: &EdgeOfFunc<'id, Self>,
1458 vars: &EdgeOfFunc<'id, Self>,
1459 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1460 apply_quant_dispatch::<_, _, { BCDDOp::Forall as u8 }, { BCDDOp::Exists as u8 }>(
1461 manager,
1462 SequentialRecursor,
1463 op,
1464 lhs.borrowed(),
1465 rhs.borrowed(),
1466 vars.borrowed(),
1467 )
1468 }
1469 #[inline]
1470 fn apply_exists_edge<'id>(
1471 manager: &Self::Manager<'id>,
1472 op: BooleanOperator,
1473 lhs: &EdgeOfFunc<'id, Self>,
1474 rhs: &EdgeOfFunc<'id, Self>,
1475 vars: &EdgeOfFunc<'id, Self>,
1476 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1477 apply_quant_dispatch::<_, _, { BCDDOp::Exists as u8 }, { BCDDOp::Forall as u8 }>(
1478 manager,
1479 SequentialRecursor,
1480 op,
1481 lhs.borrowed(),
1482 rhs.borrowed(),
1483 vars.borrowed(),
1484 )
1485 }
1486 #[inline]
1487 fn apply_unique_edge<'id>(
1488 manager: &Self::Manager<'id>,
1489 op: BooleanOperator,
1490 lhs: &EdgeOfFunc<'id, Self>,
1491 rhs: &EdgeOfFunc<'id, Self>,
1492 vars: &EdgeOfFunc<'id, Self>,
1493 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1494 let rec = SequentialRecursor;
1495 let (lhs, rhs, vars) = (lhs.borrowed(), rhs.borrowed(), vars.borrowed());
1496 apply_quant_unique_dispatch(manager, rec, op, lhs, rhs, vars)
1497 }
1498}
1499
1500impl<F: Function, T: Tag> DotStyle<T> for BCDDFunction<F> {}
1501
1502#[cfg(feature = "multi-threading")]
1503pub mod mt {
1504 use oxidd_core::HasWorkers;
1505
1506 use crate::recursor::mt::ParallelRecursor;
1507
1508 use super::*;
1509
1510 #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Function, Debug)]
1512 #[repr_id = "BCDD"]
1513 #[repr(transparent)]
1514 pub struct BCDDFunctionMT<F: Function>(F);
1515
1516 impl<F: Function> From<F> for BCDDFunctionMT<F> {
1517 #[inline(always)]
1518 fn from(value: F) -> Self {
1519 BCDDFunctionMT(value)
1520 }
1521 }
1522
1523 impl<F: Function> BCDDFunctionMT<F>
1524 where
1525 for<'id> F::Manager<'id>: HasWorkers,
1526 {
1527 #[inline(always)]
1529 pub fn into_inner(self) -> F {
1530 self.0
1531 }
1532 }
1533
1534 impl<F: Function> FunctionSubst for BCDDFunctionMT<F>
1535 where
1536 for<'id> F::Manager<'id>: Manager<Terminal = BCDDTerminal, EdgeTag = EdgeTag>
1537 + HasBCDDOpApplyCache<F::Manager<'id>>
1538 + HasWorkers,
1539 for<'id> INodeOfFunc<'id, F>: HasLevel,
1540 for<'id> EdgeOfFunc<'id, F>: Send + Sync,
1541 {
1542 fn substitute_edge<'id, 'a>(
1543 manager: &'a Self::Manager<'id>,
1544 edge: &'a EdgeOfFunc<'id, Self>,
1545 substitution: impl oxidd_core::util::Substitution<
1546 Replacement = Borrowed<'a, EdgeOfFunc<'id, Self>>,
1547 >,
1548 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1549 let subst = substitute_prepare(manager, substitution.pairs())?;
1550 let edge = edge.borrowed();
1551 let cache_id = substitution.id();
1552 let rec = ParallelRecursor::new(manager);
1553 substitute(manager, rec, edge, &subst, cache_id)
1554 }
1555 }
1556
1557 impl<F: Function> BooleanFunction for BCDDFunctionMT<F>
1558 where
1559 for<'id> F::Manager<'id>: Manager<Terminal = BCDDTerminal, EdgeTag = EdgeTag>
1560 + HasBCDDOpApplyCache<F::Manager<'id>>
1561 + HasWorkers,
1562 for<'id> INodeOfFunc<'id, F>: HasLevel,
1563 for<'id> EdgeOfFunc<'id, F>: Send + Sync,
1564 {
1565 #[inline(always)]
1566 fn var_edge<'id>(
1567 manager: &Self::Manager<'id>,
1568 var: VarNo,
1569 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1570 BCDDFunction::<F>::var_edge(manager, var)
1571 }
1572
1573 #[inline]
1574 fn f_edge<'id>(manager: &Self::Manager<'id>) -> EdgeOfFunc<'id, Self> {
1575 get_terminal(manager, false)
1576 }
1577 #[inline]
1578 fn t_edge<'id>(manager: &Self::Manager<'id>) -> EdgeOfFunc<'id, Self> {
1579 get_terminal(manager, true)
1580 }
1581
1582 #[inline]
1583 fn not_edge<'id>(
1584 manager: &Self::Manager<'id>,
1585 edge: &EdgeOfFunc<'id, Self>,
1586 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1587 Ok(not_owned(manager.clone_edge(edge)))
1588 }
1589 #[inline]
1590 fn not_edge_owned<'id>(
1591 _manager: &Self::Manager<'id>,
1592 edge: EdgeOfFunc<'id, Self>,
1593 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1594 Ok(not_owned(edge))
1595 }
1596
1597 #[inline]
1598 fn and_edge<'id>(
1599 manager: &Self::Manager<'id>,
1600 lhs: &EdgeOfFunc<'id, Self>,
1601 rhs: &EdgeOfFunc<'id, Self>,
1602 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1603 let (lhs, rhs) = (lhs.borrowed(), rhs.borrowed());
1604 apply_and(manager, ParallelRecursor::new(manager), lhs, rhs)
1605 }
1606 #[inline]
1607 fn or_edge<'id>(
1608 manager: &Self::Manager<'id>,
1609 lhs: &EdgeOfFunc<'id, Self>,
1610 rhs: &EdgeOfFunc<'id, Self>,
1611 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1612 let (nl, nr) = (not(lhs), not(rhs));
1613 let nor = apply_and(manager, ParallelRecursor::new(manager), nl, nr)?;
1614 Ok(not_owned(nor))
1615 }
1616 #[inline]
1617 fn nand_edge<'id>(
1618 manager: &Self::Manager<'id>,
1619 lhs: &EdgeOfFunc<'id, Self>,
1620 rhs: &EdgeOfFunc<'id, Self>,
1621 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1622 let (lhs, rhs) = (lhs.borrowed(), rhs.borrowed());
1623 let and = apply_and(manager, ParallelRecursor::new(manager), lhs, rhs)?;
1624 Ok(not_owned(and))
1625 }
1626 #[inline]
1627 fn nor_edge<'id>(
1628 manager: &Self::Manager<'id>,
1629 lhs: &EdgeOfFunc<'id, Self>,
1630 rhs: &EdgeOfFunc<'id, Self>,
1631 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1632 let (nl, nr) = (not(lhs), not(rhs));
1633 apply_and(manager, ParallelRecursor::new(manager), nl, nr)
1634 }
1635 #[inline]
1636 fn xor_edge<'id>(
1637 manager: &Self::Manager<'id>,
1638 lhs: &EdgeOfFunc<'id, Self>,
1639 rhs: &EdgeOfFunc<'id, Self>,
1640 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1641 let (lhs, rhs) = (lhs.borrowed(), rhs.borrowed());
1642 let rec = ParallelRecursor::new(manager);
1643 apply_bin::<_, _, { BCDDOp::Xor as u8 }>(manager, rec, lhs, rhs)
1644 }
1645 #[inline]
1646 fn equiv_edge<'id>(
1647 manager: &Self::Manager<'id>,
1648 lhs: &EdgeOfFunc<'id, Self>,
1649 rhs: &EdgeOfFunc<'id, Self>,
1650 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1651 let (lhs, rhs) = (lhs.borrowed(), rhs.borrowed());
1652 let rec = ParallelRecursor::new(manager);
1653 Ok(not_owned(apply_bin::<_, _, { BCDDOp::Xor as u8 }>(
1654 manager, rec, lhs, rhs,
1655 )?))
1656 }
1657 #[inline]
1658 fn imp_edge<'id>(
1659 manager: &Self::Manager<'id>,
1660 lhs: &EdgeOfFunc<'id, Self>,
1661 rhs: &EdgeOfFunc<'id, Self>,
1662 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1663 let (lhs, nr) = (lhs.borrowed(), not(rhs));
1665 let not_imp = apply_and(manager, ParallelRecursor::new(manager), lhs, nr)?;
1666 Ok(not_owned(not_imp))
1667 }
1668 #[inline]
1669 fn imp_strict_edge<'id>(
1670 manager: &Self::Manager<'id>,
1671 lhs: &EdgeOfFunc<'id, Self>,
1672 rhs: &EdgeOfFunc<'id, Self>,
1673 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1674 let (nl, rhs) = (not(lhs), rhs.borrowed());
1675 apply_and(manager, ParallelRecursor::new(manager), nl, rhs)
1676 }
1677
1678 #[inline]
1679 fn ite_edge<'id>(
1680 manager: &Self::Manager<'id>,
1681 f: &EdgeOfFunc<'id, Self>,
1682 g: &EdgeOfFunc<'id, Self>,
1683 h: &EdgeOfFunc<'id, Self>,
1684 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1685 let (f, g, h) = (f.borrowed(), g.borrowed(), h.borrowed());
1686 apply_ite(manager, ParallelRecursor::new(manager), f, g, h)
1687 }
1688
1689 #[inline]
1690 fn sat_count_edge<'id, N: SatCountNumber, S: std::hash::BuildHasher>(
1691 manager: &Self::Manager<'id>,
1692 edge: &EdgeOfFunc<'id, Self>,
1693 vars: LevelNo,
1694 cache: &mut SatCountCache<N, S>,
1695 ) -> N {
1696 BCDDFunction::<F>::sat_count_edge(manager, edge, vars, cache)
1697 }
1698
1699 #[inline]
1700 fn pick_cube_edge<'id>(
1701 manager: &Self::Manager<'id>,
1702 edge: &EdgeOfFunc<'id, Self>,
1703 choice: impl FnMut(&Self::Manager<'id>, &EdgeOfFunc<'id, Self>, LevelNo) -> bool,
1704 ) -> Option<Vec<OptBool>> {
1705 BCDDFunction::<F>::pick_cube_edge(manager, edge, choice)
1706 }
1707 #[inline]
1708 fn pick_cube_dd_edge<'id>(
1709 manager: &Self::Manager<'id>,
1710 edge: &EdgeOfFunc<'id, Self>,
1711 choice: impl FnMut(&Self::Manager<'id>, &EdgeOfFunc<'id, Self>, LevelNo) -> bool,
1712 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1713 BCDDFunction::<F>::pick_cube_dd_edge(manager, edge, choice)
1714 }
1715 #[inline]
1716 fn pick_cube_dd_set_edge<'id>(
1717 manager: &Self::Manager<'id>,
1718 edge: &EdgeOfFunc<'id, Self>,
1719 literal_set: &EdgeOfFunc<'id, Self>,
1720 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1721 BCDDFunction::<F>::pick_cube_dd_set_edge(manager, edge, literal_set)
1722 }
1723
1724 #[inline]
1725 fn eval_edge<'id>(
1726 manager: &Self::Manager<'id>,
1727 edge: &EdgeOfFunc<'id, Self>,
1728 args: impl IntoIterator<Item = (VarNo, bool)>,
1729 ) -> bool {
1730 BCDDFunction::<F>::eval_edge(manager, edge, args)
1731 }
1732 }
1733
1734 impl<F: Function> BooleanFunctionQuant for BCDDFunctionMT<F>
1735 where
1736 for<'id> F::Manager<'id>: Manager<Terminal = BCDDTerminal, EdgeTag = EdgeTag>
1737 + HasBCDDOpApplyCache<F::Manager<'id>>
1738 + HasWorkers,
1739 for<'id> INodeOfFunc<'id, F>: HasLevel,
1740 for<'id> EdgeOfFunc<'id, F>: Send + Sync,
1741 {
1742 #[inline]
1743 fn restrict_edge<'id>(
1744 manager: &Self::Manager<'id>,
1745 root: &EdgeOfFunc<'id, Self>,
1746 vars: &EdgeOfFunc<'id, Self>,
1747 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1748 let (root, vars) = (root.borrowed(), vars.borrowed());
1749 restrict(manager, ParallelRecursor::new(manager), root, vars)
1750 }
1751
1752 #[inline]
1753 fn forall_edge<'id>(
1754 manager: &Self::Manager<'id>,
1755 root: &EdgeOfFunc<'id, Self>,
1756 vars: &EdgeOfFunc<'id, Self>,
1757 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1758 let (root, vars) = (root.borrowed(), vars.borrowed());
1759 let rec = ParallelRecursor::new(manager);
1760 quant::<_, _, { BCDDOp::Forall as u8 }>(manager, rec, root, vars)
1761 }
1762 #[inline]
1763 fn exists_edge<'id>(
1764 manager: &Self::Manager<'id>,
1765 root: &EdgeOfFunc<'id, Self>,
1766 vars: &EdgeOfFunc<'id, Self>,
1767 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1768 let (root, vars) = (root.borrowed(), vars.borrowed());
1769 let rec = ParallelRecursor::new(manager);
1770 quant::<_, _, { BCDDOp::Exists as u8 }>(manager, rec, root, vars)
1771 }
1772 #[inline]
1773 fn unique_edge<'id>(
1774 manager: &Self::Manager<'id>,
1775 root: &EdgeOfFunc<'id, Self>,
1776 vars: &EdgeOfFunc<'id, Self>,
1777 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1778 let (root, vars) = (root.borrowed(), vars.borrowed());
1779 let rec = ParallelRecursor::new(manager);
1780 quant::<_, _, { BCDDOp::Unique as u8 }>(manager, rec, root, vars)
1781 }
1782
1783 #[inline]
1784 fn apply_forall_edge<'id>(
1785 manager: &Self::Manager<'id>,
1786 op: BooleanOperator,
1787 lhs: &EdgeOfFunc<'id, Self>,
1788 rhs: &EdgeOfFunc<'id, Self>,
1789 vars: &EdgeOfFunc<'id, Self>,
1790 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1791 let (lhs, rhs, vars) = (lhs.borrowed(), rhs.borrowed(), vars.borrowed());
1792 apply_quant_dispatch::<_, _, { BCDDOp::Forall as u8 }, { BCDDOp::Exists as u8 }>(
1793 manager,
1794 ParallelRecursor::new(manager),
1795 op,
1796 lhs,
1797 rhs,
1798 vars,
1799 )
1800 }
1801 #[inline]
1802 fn apply_exists_edge<'id>(
1803 manager: &Self::Manager<'id>,
1804 op: BooleanOperator,
1805 lhs: &EdgeOfFunc<'id, Self>,
1806 rhs: &EdgeOfFunc<'id, Self>,
1807 vars: &EdgeOfFunc<'id, Self>,
1808 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1809 let (lhs, rhs, vars) = (lhs.borrowed(), rhs.borrowed(), vars.borrowed());
1810 apply_quant_dispatch::<_, _, { BCDDOp::Exists as u8 }, { BCDDOp::Forall as u8 }>(
1811 manager,
1812 ParallelRecursor::new(manager),
1813 op,
1814 lhs,
1815 rhs,
1816 vars,
1817 )
1818 }
1819 #[inline]
1820 fn apply_unique_edge<'id>(
1821 manager: &Self::Manager<'id>,
1822 op: BooleanOperator,
1823 lhs: &EdgeOfFunc<'id, Self>,
1824 rhs: &EdgeOfFunc<'id, Self>,
1825 vars: &EdgeOfFunc<'id, Self>,
1826 ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1827 let (lhs, rhs, vars) = (lhs.borrowed(), rhs.borrowed(), vars.borrowed());
1828 let rec = ParallelRecursor::new(manager);
1829 apply_quant_unique_dispatch(manager, rec, op, lhs, rhs, vars)
1830 }
1831 }
1832
1833 impl<F: Function, T: Tag> DotStyle<T> for BCDDFunctionMT<F> {}
1834}