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