oxidd_core/lib.rs
1//! Collection of fundamental traits and types to represent decision diagrams
2//!
3//! # Overview
4//!
5//! One of the most central traits is [`Manager`]. The manager is responsible
6//! for storing the nodes of a decision diagram ([`InnerNode`]s and terminal
7//! nodes) and provides [`Edge`]s to identify them.
8//!
9//! From the user's perspective, [`Function`][function::Function] is very
10//! important. A function is some kind of external reference to a node and is
11//! the basis for assigning semantics to nodes and providing operations such as
12//! applying connectives of boolean logic.
13
14#![warn(missing_docs)]
15#![deny(unsafe_op_in_unsafe_fn)]
16#![allow(clippy::double_must_use)]
17// Explicitly writing out `'id` lifetimes possibly makes some code easier to
18// read.
19#![allow(clippy::needless_lifetimes)]
20// `match` syntax may be easier to read
21#![allow(clippy::manual_map)]
22
23use std::borrow::Borrow;
24use std::hash::Hash;
25use std::ops::Range;
26
27pub mod error;
28pub mod function;
29pub mod util;
30
31use error::DuplicateVarName;
32use util::{AllocResult, Borrowed, DropWith, NodeSet};
33
34/// Manager reference
35///
36/// The methods of this trait synchronize accesses to the manager: In a
37/// concurrent setting, a manager has some kind of read/write lock, and
38/// [`Self::with_manager_shared()`] / [`Self::with_manager_exclusive()`] acquire
39/// this lock accordingly. In a sequential implementation, a
40/// [`RefCell`][std::cell::RefCell] or the like may be used instead of lock.
41pub trait ManagerRef: Clone + Eq + Hash + for<'a, 'id> From<&'a Self::Manager<'id>> {
42 /// Type of the associated manager
43 ///
44 /// For more details on why this type is generic over `'id`, see the
45 /// documentation of [`Function::Manager`][function::Function::Manager].
46 type Manager<'id>: Manager;
47
48 /// Obtain a shared manager reference
49 ///
50 /// Locking behavior: acquires the manager's lock for shared access.
51 fn with_manager_shared<F, T>(&self, f: F) -> T
52 where
53 F: for<'id> FnOnce(&Self::Manager<'id>) -> T;
54
55 /// Obtain an exclusive manager reference
56 ///
57 /// Locking behavior: acquires the manager's lock for exclusive access.
58 fn with_manager_exclusive<F, T>(&self, f: F) -> T
59 where
60 F: for<'id> FnOnce(&mut Self::Manager<'id>) -> T;
61}
62
63/// Reduction rules for decision diagrams
64///
65/// This trait is intended to be implemented on a zero-sized type. Refer to
66/// [`DiagramRules::reduce()`] for an example implementation.
67pub trait DiagramRules<E: Edge, N: InnerNode<E>, T> {
68 /// Iterator created by [`DiagramRules::cofactors()`]
69 type Cofactors<'a>: Iterator<Item = Borrowed<'a, E>>
70 where
71 E: 'a,
72 N: 'a;
73
74 /// Apply the reduction rule(s)
75 ///
76 /// Besides uniqueness of nodes (there are no two nodes with the same
77 /// children at the same level), decision diagrams typically impose some
78 /// other reduction rules. The former is provided by the [`Manager`] /
79 /// [`LevelView`]s, the latter is implemented here.
80 ///
81 /// The implementation is responsible for consuming the entire `children`
82 /// iterator and dropping unused edges.
83 ///
84 /// # Example implementation
85 ///
86 /// In binary decision diagrams (BDDs), there are no nodes with equal
87 /// children. An implementation might look like this:
88 ///
89 /// ```
90 /// # use oxidd_core::{*, util::BorrowedEdgeIter};
91 /// struct BDDRules;
92 /// impl<E: Edge, N: InnerNode<E>, T> DiagramRules<E, N, T> for BDDRules {
93 /// type Cofactors<'a> = N::ChildrenIter<'a> where N: 'a, E: 'a;
94 ///
95 /// fn reduce<M: Manager<Edge = E, InnerNode = N, Terminal = T>>(
96 /// manager: &M,
97 /// level: LevelNo,
98 /// children: impl IntoIterator<Item = E>,
99 /// ) -> ReducedOrNew<E, N> {
100 /// let mut it = children.into_iter();
101 /// let f0 = it.next().unwrap();
102 /// let f1 = it.next().unwrap();
103 /// debug_assert!(it.next().is_none());
104 ///
105 /// if f0 == f1 {
106 /// manager.drop_edge(f1);
107 /// ReducedOrNew::Reduced(f0)
108 /// } else {
109 /// ReducedOrNew::New(InnerNode::new(level, [f0, f1]), Default::default())
110 /// }
111 /// }
112 ///
113 /// fn cofactors(_tag: E::Tag, node: &N) -> Self::Cofactors<'_> {
114 /// node.children()
115 /// }
116 /// }
117 /// ```
118 ///
119 /// Note that we assume no complemented edges, hence the cofactor iterator
120 /// is just `node.children()`.
121 ///
122 /// The implementation assumes that there are no panics, otherwise it would
123 /// leak some edges. It might be a bit better to use
124 /// [`EdgeDropGuard`][util::EdgeDropGuard]s, but this would make the code
125 /// more clumsy, and our assumption is usually fair enough.
126 fn reduce<M: Manager<Edge = E, InnerNode = N, Terminal = T>>(
127 manager: &M,
128 level: LevelNo,
129 children: impl IntoIterator<Item = E>,
130 ) -> ReducedOrNew<E, N>;
131
132 /// Get the cofactors of `node` assuming an incoming edge with `tag`
133 ///
134 /// In some diagram types, this is the same as [`InnerNode::children()`].
135 /// However, in a binary decision diagram with complement edges, we need to
136 /// respect the tag of the incoming edge: If the incoming edge is
137 /// complemented, then we need to complement the outgoing edges as well.
138 fn cofactors(tag: E::Tag, node: &N) -> Self::Cofactors<'_>;
139
140 /// Get the `n`-th cofactor of `node` assuming an incoming edge with `tag`
141 ///
142 /// This is equivalent to `Self::cofactors(tag, node).nth(n).unwrap()`.
143 #[inline]
144 fn cofactor(tag: E::Tag, node: &N, n: usize) -> Borrowed<'_, E> {
145 Self::cofactors(tag, node).nth(n).expect("out of range")
146 }
147}
148
149/// Result of the attempt to create a new node
150///
151/// Before actually creating a new node, reduction rules should be applied
152/// (see [`DiagramRules::reduce()`]). If a reduction was applied, then
153/// [`DiagramRules::reduce()`] returns the `Reduced` variant, otherwise the
154/// `New` variant.
155pub enum ReducedOrNew<E: Edge, N: InnerNode<E>> {
156 /// A reduction rule was applied
157 Reduced(E),
158 /// The node is new. After inserting it into the manager, the edge should be
159 /// tagged with the given tag.
160 New(N, E::Tag),
161}
162
163impl<E: Edge, N: InnerNode<E>> ReducedOrNew<E, N> {
164 /// Insert `self` into `manager` and `unique_table` at the given `level` if
165 /// it is `New`, otherwise return the `Reduced` edge.
166 ///
167 /// `level` must agree with the level used for creating the node, and must
168 /// be strictly above (i.e. less than) the children's levels.
169 #[must_use]
170 #[inline(always)]
171 pub fn then_insert<M>(self, manager: &M, level: LevelNo) -> AllocResult<E>
172 where
173 M: Manager<InnerNode = N, Edge = E>,
174 {
175 match self {
176 ReducedOrNew::Reduced(e) => Ok(e),
177 ReducedOrNew::New(node, tag) => {
178 debug_assert_ne!(level, LevelNo::MAX);
179 debug_assert!(node.check_level(|l| l == level));
180 debug_assert!(node.children().all(|c| {
181 if let Node::Inner(node) = manager.get_node(&*c) {
182 node.check_level(|l| level < l)
183 } else {
184 true
185 }
186 }));
187
188 let edge = manager.level(level).get_or_insert(node)?;
189 Ok(edge.with_tag_owned(tag))
190 }
191 }
192 }
193}
194
195/// Node in a decision diagram
196///
197/// [`Eq`] and [`Hash`] should consider the children only, in particular no
198/// level information. This means that if `Self` implements [`HasLevel`],
199/// [`HasLevel::set_level()`] may be called while the node is present in a
200/// [`LevelView`]. This is not the case for [`InnerNode::set_child()`]: The user
201/// must remove the node from the [`LevelView`] before setting the children (and
202/// re-insert it afterwards).
203#[must_use]
204pub trait InnerNode<E: Edge>: Sized + Eq + Hash + DropWith<E> {
205 /// The node's arity (upper bound)
206 const ARITY: usize;
207
208 /// Iterator over children of an inner node
209 type ChildrenIter<'a>: ExactSizeIterator<Item = Borrowed<'a, E>>
210 where
211 Self: 'a,
212 E: 'a;
213
214 /// Create a new node
215 ///
216 /// Note that this does not apply any reduction rules. A node type that does
217 /// not store levels internally (does not implement [`HasLevel`]) may simply
218 /// ignore the `level` parameter.
219 ///
220 /// Panics if `children`'s length does not match the node's requirements
221 /// (typically, the length should be [`Self::ARITY`], but some node types
222 /// may deviate from that).
223 #[must_use]
224 fn new(level: LevelNo, children: impl IntoIterator<Item = E>) -> Self;
225
226 /// Returns the result of `check` applied to the node's level in case this
227 /// node type stores levels, otherwise returns `true`.
228 ///
229 /// Use [`HasLevel::level()`] if you require your nodes to store the level
230 /// number and want to get the level number.
231 fn check_level(&self, check: impl FnOnce(LevelNo) -> bool) -> bool;
232
233 /// Panics if the node types stores a level and the node's level is not
234 /// `level`
235 fn assert_level_matches(&self, level: LevelNo);
236
237 /// Get the children of this node as an iterator
238 #[must_use]
239 fn children(&self) -> Self::ChildrenIter<'_>;
240
241 /// Get the `n`-th child of this node
242 fn child(&self, n: usize) -> Borrowed<'_, E>;
243
244 /// Set the `n`-th child of this node
245 ///
246 /// Returns the previous `n`-th child.
247 ///
248 /// Note that this function may also change the node's hash value etc., so
249 /// in case the node is stored in a hash table ([`LevelView`]), it needs to
250 /// be removed before calling this method.
251 ///
252 /// Panics if the node does not have an `n`-th child.
253 ///
254 /// # Safety
255 ///
256 /// The caller must have exclusive access to the node. In the first place,
257 /// this is granted by acquiring an exclusive manager lock
258 /// ([`Function::with_manager_exclusive()`][function::Function::with_manager_exclusive] or
259 /// [`ManagerRef::with_manager_exclusive()`]). However, exclusive access to
260 /// some nodes may be delegated to other threads (which is the reason why we
261 /// only require a shared and not a mutable manager reference). Furthermore,
262 /// there must not be a borrowed child (obtained via
263 /// [`InnerNode::children()`]).
264 #[must_use = "call `Manager::drop_edge()` if you don't need the previous edge"]
265 unsafe fn set_child(&self, n: usize, child: E) -> E;
266
267 /// Get the node's reference count
268 ///
269 /// This ignores all internal references used by the manager
270 /// implementation.
271 #[must_use]
272 fn ref_count(&self) -> usize;
273}
274
275/// Level number type
276///
277/// Levels with lower numbers are located at the top of the diagram, higher
278/// numbers at the bottom. [`LevelNo::MAX`] is reserved for terminal nodes.
279/// Adjacent levels have adjacent level numbers.
280pub type LevelNo = u32;
281
282/// Atomic version of [`LevelNo`]
283pub type AtomicLevelNo = std::sync::atomic::AtomicU32;
284
285/// Variable number type
286pub type VarNo = LevelNo;
287
288/// Atomic version of [`VarNo`]
289pub type AtomicVarNo = AtomicLevelNo;
290
291/// Trait for nodes that have a level
292///
293/// Quasi-reduced BDDs, for instance, do not need the level information stored
294/// in their nodes, so there is no need to implement this trait.
295///
296/// Implementors should also implement [`InnerNode`]. If `Self` is [`Sync`],
297/// then the level number should be implemented using [`AtomicLevelNo`]. In
298/// particular, concurrent calls to [`Self::level()`] and [`Self::set_level()`]
299/// must not lead to data races.
300///
301/// # Safety
302///
303/// 1. A node in a [`LevelView`] with level number L has level number L (i.e.
304/// `self.level()` returns L).
305/// 2. [`InnerNode::check_level()`] with a check `c` must return
306/// `c(self.level())`. Similarly, [`InnerNode::assert_level_matches()`] must
307/// panic if the level does not match.
308///
309/// These conditions are crucial to enable concurrent level swaps as part of
310/// reordering (see the `oxidd-reorder` crate): The algorithm iterates over the
311/// nodes at the upper level and needs to know whether a node is part of the
312/// level directly below it. The procedure only has access to nodes at these two
313/// levels, hence it must rely on the level information for SAFETY.
314///
315/// Note that invariant 1 may be broken by [`HasLevel::set_level()`] and
316/// [`LevelView::swap()`]; the caller of these functions is responsible to
317/// re-establish the invariant.
318pub unsafe trait HasLevel {
319 /// Get the node's level
320 #[must_use]
321 fn level(&self) -> LevelNo;
322
323 /// Set the node's level
324 ///
325 /// # Safety
326 ///
327 /// This method may break SAFETY invariant 1 of the [`HasLevel`] trait: A
328 /// node in a [`LevelView`] with level number L has level number L (i.e.
329 /// `self.level()` returns L). The caller is responsible to re-establish the
330 /// invariant. (Make sure that the calling code is exception-safe!)
331 unsafe fn set_level(&self, level: LevelNo);
332}
333
334/// Node identifier returned by [`Edge::node_id()`]
335///
336/// The most significant bit is reserved, i.e. `NodeID`s must (normally) be less
337/// than `1 << (NodeID::BITS - 1)`
338pub type NodeID = usize;
339
340/// Edge in a decision diagram
341///
342/// Generally speaking, an edge is the directed connection between two nodes,
343/// with some kind of annotation. In a binary decision diagram with complement
344/// edges, there are the "true" edges as well as the normal and the complemented
345/// "false" edges. When considering a single edge, it usually is not so
346/// important whether this edge is a "true" or "false" edge; we can simply have
347/// distinguishable "slots" in the source node. In contrast, whether an edge is
348/// complemented or not has a much greater influence on the meaning of an edge.
349///
350/// In a decision diagram, obtaining the source of an edge is usually not so
351/// important, hence this trait does not provide such functionality. This means
352/// that an edge can (more or less) be implemented as a (tagged) pointer to the
353/// target node.
354///
355/// This trait requires implementors to also implement [`Ord`]. Edges should be
356/// considered equal if and only if they point to the same node with the same
357/// tag. Besides that, there are no further restrictions. The order implemented
358/// for [`Ord`] can be an arbitrary, fixed order (e.g. using addresses of the
359/// nodes). The main idea of this is to give the set `{f, g}` of two edges `f`
360/// and `g` a unique tuple/array representation.
361#[must_use]
362pub trait Edge: Sized + Ord + Hash {
363 /// Edge tag
364 ///
365 /// For instance, an edge tag can be used to mark an edge as complemented.
366 ///
367 /// If the decision diagram does not need any special edge tags, this can
368 /// simply be `()`.
369 type Tag: Tag;
370
371 /// Turn a reference into a borrowed handle
372 fn borrowed(&self) -> Borrowed<'_, Self>;
373 /// Get a version of this [`Edge`] with the given tag
374 ///
375 /// Refer to [`Borrowed::edge_with_tag()`] for cases in which this method
376 /// cannot be used due to lifetime restrictions.
377 fn with_tag(&self, tag: Self::Tag) -> Borrowed<'_, Self>;
378 /// Get a version of this [`Edge`] with the given tag
379 fn with_tag_owned(self, tag: Self::Tag) -> Self;
380
381 /// Get the [`Tag`] of this [`Edge`]
382 fn tag(&self) -> Self::Tag;
383
384 /// Returns some unique identifier for the node, e.g. for I/O purposes
385 fn node_id(&self) -> NodeID;
386}
387
388/// Trait for tags that can be attached to pointers (e.g. edges, see
389/// [`Edge::Tag`])
390///
391/// This trait is automatically implemented for types that implement [`Eq`],
392/// [`Default`], and [`Countable`].
393pub trait Tag: Sized + Copy + Eq + Default + Countable {}
394impl<T: Eq + Default + Countable> Tag for T {}
395
396/// Types whose values can be counted, i.e. there is a bijection between the
397/// values of the type and the range `0..=MAX_VALUE`.
398///
399/// This is mainly intended to be implemented on `enum`s. In most cases, you can
400/// simply derive it.
401///
402/// # Safety
403///
404/// [`Countable::as_usize()`] and [`Countable::from_usize()`] must form a
405/// bijection between the values of type `Self` and `0..=MAX_VALUE`, more
406/// formally: For all `t: Self` it must hold that
407/// `Self::from_usize(t.as_usize()) == t`.
408/// For all `u: usize` such that `t.as_usize() == u` for some `t: Self`,
409/// `Self::from_usize(u).as_usize() == u` must be true. Furthermore,
410/// `t.as_usize() <= Self::MAX_VALUE` must hold.
411///
412/// This trait is marked unsafe because violating any invariant of the above may
413/// e.g. result in out-of-bounds accesses.
414pub unsafe trait Countable: Sized + Copy {
415 /// Maximum value returned by `self.as_usize()`.
416 const MAX_VALUE: usize;
417
418 /// Convert `self` into a `usize`.
419 #[must_use]
420 fn as_usize(self) -> usize;
421 /// Convert the given `value` into an instance of `Self`.
422 ///
423 /// May panic if an invalid value is passed, or return some default value.
424 #[must_use]
425 fn from_usize(value: usize) -> Self;
426}
427
428// SAFETY: There is a bijection for all values of type `()` and `0..=MAX_VALUE`.
429unsafe impl Countable for () {
430 const MAX_VALUE: usize = 0;
431
432 #[inline]
433 fn as_usize(self) -> usize {
434 0
435 }
436
437 #[inline]
438 fn from_usize(_value: usize) -> Self {}
439}
440
441// SAFETY: There is a bijection for all values of type `bool` and `0..=1`.
442unsafe impl Countable for bool {
443 const MAX_VALUE: usize = 1;
444
445 #[inline]
446 fn as_usize(self) -> usize {
447 self as usize
448 }
449
450 #[inline]
451 fn from_usize(value: usize) -> Self {
452 value != 0
453 }
454}
455
456/// Either an inner or a terminal node
457pub enum Node<'a, M: Manager + 'a> {
458 #[allow(missing_docs)]
459 Inner(&'a M::InnerNode),
460 #[allow(missing_docs)]
461 Terminal(M::TerminalRef<'a>),
462}
463
464impl<'a, M: Manager + 'a> Clone for Node<'a, M> {
465 fn clone(&self) -> Self {
466 *self
467 }
468}
469impl<'a, M: Manager + 'a> Copy for Node<'a, M> {}
470
471impl<'a, M: Manager> Node<'a, M> {
472 /// Get the reference count of the underlying node
473 #[must_use]
474 #[inline]
475 pub fn ref_count(self) -> usize {
476 match self {
477 Node::Inner(node) => node.ref_count(),
478 Node::Terminal(_) => usize::MAX, /* TODO: should we support concrete reference counts
479 * for terminals? */
480 }
481 }
482}
483
484impl<'a, M: Manager> Node<'a, M>
485where
486 M::InnerNode: HasLevel,
487{
488 /// Get the level of the underlying node (`LevelNo::MAX` for terminals)
489 #[must_use]
490 #[inline]
491 pub fn level(self) -> LevelNo {
492 match self {
493 Node::Inner(node) => node.level(),
494 Node::Terminal(_) => LevelNo::MAX,
495 }
496 }
497}
498
499impl<'a, M: Manager> Node<'a, M> {
500 /// Unwrap the inner node
501 ///
502 /// Panics if `self` is a terminal.
503 #[must_use]
504 #[track_caller]
505 #[inline]
506 pub fn unwrap_inner(self) -> &'a M::InnerNode {
507 match self {
508 Node::Inner(node) => node,
509 Node::Terminal(_) => panic!("expected an inner node, but this is a terminal"),
510 }
511 }
512
513 /// Unwrap the inner node
514 ///
515 /// Panics with `msg` if `self` is a terminal.
516 #[must_use]
517 #[track_caller]
518 #[inline]
519 pub fn expect_inner(self, msg: &str) -> &'a M::InnerNode {
520 match self {
521 Node::Inner(node) => node,
522 Node::Terminal(_) => panic!("{}", msg),
523 }
524 }
525
526 /// Returns `true` if this is an inner node
527 #[inline]
528 pub fn is_inner(self) -> bool {
529 match self {
530 Node::Inner(_) => true,
531 Node::Terminal(_) => false,
532 }
533 }
534
535 /// Unwrap the terminal
536 ///
537 /// Panics if `self` is an inner node
538 #[must_use]
539 #[track_caller]
540 #[inline]
541 pub fn unwrap_terminal(&self) -> &M::Terminal {
542 match self {
543 Node::Inner(_) => panic!("expected a terminal node, but this is an inner node"),
544 Node::Terminal(t) => t.borrow(),
545 }
546 }
547
548 /// Unwrap the terminal
549 ///
550 /// Panics with `msg` if `self` is an inner node
551 #[must_use]
552 #[track_caller]
553 #[inline]
554 pub fn expect_terminal(&self, msg: &str) -> &M::Terminal {
555 match self {
556 Node::Inner(_) => panic!("{}", msg),
557 Node::Terminal(t) => t.borrow(),
558 }
559 }
560
561 /// Returns `true` if this is any terminal node
562 #[inline]
563 pub fn is_any_terminal(self) -> bool {
564 match self {
565 Node::Inner(_) => false,
566 Node::Terminal(_) => true,
567 }
568 }
569
570 /// Returns `true` if this is the given `terminal`
571 #[inline]
572 pub fn is_terminal(self, terminal: &M::Terminal) -> bool {
573 match self {
574 Node::Inner(_) => false,
575 Node::Terminal(t) => t.borrow() == terminal,
576 }
577 }
578}
579
580/// Manager for nodes in a decision diagram
581///
582/// In the basic formulation, a decision diagram is a directed acyclic graph
583/// where all inner nodes are associated with a level, and each level in turn is
584/// associated with a variable. A decision diagram can represent functions
585/// Dⁿ → T, where n is the number of variables. Every inner node has |D|
586/// outgoing edges, pointing to child nodes. The semantics of an inner node
587/// depends on the value of its associated variable. If the variable has
588/// value d ∈ D, then the node's semantics is the semantics of the child
589/// referenced by the edge corresponding to d. Every terminal node is associated
590/// with a value t ∈ T, and the node's semantics is just that value t. Some
591/// kinds of decision diagrams deviate from this formulation in one way or the
592/// other, but still fit into the framework.
593///
594/// The manager's responsibility is to store nodes and provide edges to identify
595/// them. Internally, it has a unique table to ensure uniqueness of nodes
596/// (typically, there should be no two nodes with the same children at the same
597/// level). The manager supports some kind of garbage collection: If there are
598/// no more edges pointing to a node, the node does not necessarily have to be
599/// deleted immediately. It may well be that the node revives. But from time to
600/// time it may make sense to remove all currently dead nodes. The manager also
601/// supports a levelized view on the diagram (via [`LevelView`]s).
602///
603/// Note that we are way more concerned about levels than about variables here.
604/// This is because variables can easily be handled externally. For many kinds
605/// of diagrams, we can just use the function representation of a variable and
606/// get the mapping between variables and levels "for free". This is especially
607/// nice as there are reordering operations which change the mapping between
608/// levels and variables but implicitly update the function representations
609/// accordingly.
610///
611/// # Safety
612///
613/// The implementation must ensure that every inner node only refers to nodes
614/// stored in this manager.
615///
616/// Every level view is associated with a manager and a level number.
617/// [`Manager::level()`] must always return the level view associated to this
618/// manager with the given level number.
619pub unsafe trait Manager: Sized {
620 /// Type of edge
621 type Edge: Edge<Tag = Self::EdgeTag>;
622 /// Type of edge tags
623 type EdgeTag: Tag;
624 /// Type of inner nodes
625 type InnerNode: InnerNode<Self::Edge>;
626 /// Type of terminals
627 type Terminal: Eq + Hash;
628 /// References to [`Self::Terminal`]s
629 ///
630 /// Should either be a `&'a Self::Terminal` or just `Self::Terminal`. The
631 /// latter is useful for "static terminal managers" which don't actually
632 /// store terminal nodes but can map between edges and terminal nodes on the
633 /// fly. In this case, it would be hard to hand out node references.
634 type TerminalRef<'a>: Borrow<Self::Terminal> + Copy
635 where
636 Self: 'a;
637 /// Diagram rules, see [`DiagramRules`] for more details
638 type Rules: DiagramRules<Self::Edge, Self::InnerNode, Self::Terminal>;
639
640 /// Iterator over all terminals
641 ///
642 /// The actual items are edges pointing to terminals since this allows us to
643 /// get a [`NodeID`].
644 type TerminalIterator<'a>: Iterator<Item = Self::Edge>
645 where
646 Self: 'a;
647
648 /// Node set type, possibly more efficient than a `HashSet<NodeID>`
649 type NodeSet: NodeSet<Self::Edge>;
650
651 /// A view on a single level of the unique table.
652 type LevelView<'a>: LevelView<Self::Edge, Self::InnerNode>
653 where
654 Self: 'a;
655
656 /// Iterator over levels
657 type LevelIterator<'a>: DoubleEndedIterator<Item = Self::LevelView<'a>> + ExactSizeIterator
658 where
659 Self: 'a;
660
661 /// Get a reference to the node to which `edge` points
662 #[must_use]
663 fn get_node(&self, edge: &Self::Edge) -> Node<'_, Self>;
664
665 /// Clone `edge`
666 #[must_use]
667 fn clone_edge(&self, edge: &Self::Edge) -> Self::Edge;
668
669 /// Drop `edge`
670 fn drop_edge(&self, edge: Self::Edge);
671 /// Drop `edge` and try to remove the node it points to
672 ///
673 /// `level` is the node's level. This is required because nodes do not
674 /// necessarily store their level, but the lookup in a unique table split by
675 /// levels needs the level.
676 ///
677 /// Returns whether the node has been removed. There are multiple reasons
678 /// why removing the node can fail. Obviously, it could still be referenced
679 /// by other edges. It might also be that removing nodes is currently not
680 /// possible, e.g., because the manager is not prepared for it. Also, if
681 /// `level` does not match the node's actual level, the node referenced by
682 /// `edge` may not be removed. In this case, the method might even remove
683 /// another node with the same children which is only referenced from the
684 /// unique table.
685 ///
686 /// Passing the wrong `level` is considered to be a programming mistake. To
687 /// aid debugging, the implementation is allowed (but not required) to panic
688 /// if it can diagnose such a mistake.
689 fn try_remove_node(&self, edge: Self::Edge, level: LevelNo) -> bool;
690
691 /// Get the count of inner nodes
692 #[must_use]
693 fn num_inner_nodes(&self) -> usize;
694
695 /// Get an approximate count of inner nodes
696 ///
697 /// For concurrent implementations, it may be much less costly to determine
698 /// an approximation of the inner node count than an accurate count
699 /// ([`Self::num_inner_nodes()`]).
700 #[must_use]
701 fn approx_num_inner_nodes(&self) -> usize {
702 self.num_inner_nodes()
703 }
704
705 /// Get the number of variables
706 ///
707 /// Same as [`Self::num_levels()`]
708 #[must_use]
709 #[inline(always)]
710 fn num_vars(&self) -> VarNo {
711 self.num_levels()
712 }
713
714 /// Get the number of levels
715 ///
716 /// Same as [`Self::num_vars()`]
717 #[must_use]
718 fn num_levels(&self) -> LevelNo;
719
720 /// Get the number of named variables
721 #[must_use]
722 fn num_named_vars(&self) -> VarNo;
723
724 /// Add `additional` unnamed variables to the decision diagram
725 ///
726 /// The new variables are added at the bottom of the variable order. More
727 /// precisely, the level number equals the variable number for each new
728 /// variable.
729 ///
730 /// Note that some algorithms may assume that the domain of a function
731 /// represented by a decision diagram is just the set of all variables. In
732 /// this regard, adding variables can change the semantics of decision
733 /// diagram nodes.
734 ///
735 /// Returns the range of new variable numbers.
736 ///
737 /// Panics if [`self.num_vars()`][Self::num_vars()] plus `additional` is
738 /// greater than to [`VarNo::MAX`].
739 fn add_vars(&mut self, additional: VarNo) -> Range<VarNo>;
740
741 /// Add named variables to the decision diagram
742 ///
743 /// This is a shorthand for [`Self::add_vars()`] and respective
744 /// [`Self::set_var_name()`] calls. More details can be found there.
745 ///
746 /// Returns the range of new variable numbers on success. In case a name
747 /// is not unique (and not `""`), only the first variables with unique names
748 /// are added, and a [`DuplicateVarName`] error provides more details.
749 ///
750 /// Panics if there would be more than [`VarNo::MAX`] variables after adding
751 /// the ones from `names`.
752 fn add_named_vars<S: Into<String>>(
753 &mut self,
754 names: impl IntoIterator<Item = S>,
755 ) -> Result<Range<VarNo>, DuplicateVarName>;
756
757 /// Add named variables to the decision diagram
758 ///
759 /// This is a possibly specialized version of [`Self::add_named_vars()`].
760 ///
761 /// Panics if there would be more than [`VarNo::MAX`] variables after adding
762 /// the ones from `names`.
763 fn add_named_vars_from_map(
764 &mut self,
765 map: crate::util::VarNameMap,
766 ) -> Result<Range<VarNo>, DuplicateVarName> {
767 self.add_named_vars(map.into_names_iter())
768 }
769
770 /// Get `var`'s name
771 ///
772 /// For unnamed variables, this will return the empty string.
773 ///
774 /// Panics if `var` is greater or equal to the number of variables in this
775 /// manager.
776 fn var_name(&self, var: VarNo) -> &str;
777
778 /// Label `var` as `name`
779 ///
780 /// An empty name means that the variable will become unnamed, and cannot be
781 /// retrieved via [`Self::name_to_var()`] anymore.
782 ///
783 /// Returns a [`DuplicateVarName`] error if `name` is not unique (and not
784 /// `""`).
785 ///
786 /// Panics if `var` is greater or equal to the number of variables in this
787 /// manager.
788 fn set_var_name(&mut self, var: VarNo, name: impl Into<String>)
789 -> Result<(), DuplicateVarName>;
790
791 /// Get the variable number for the given variable name, if present
792 ///
793 /// Note that you cannot retrieve unnamed variables.
794 /// `manager.name_to_var("")` always returns `None`.
795 fn name_to_var(&self, name: impl AsRef<str>) -> Option<VarNo>;
796
797 /// Get the level for the given variable
798 ///
799 /// Panics if `level >= self.num_vars()`.
800 fn var_to_level(&self, var: VarNo) -> LevelNo;
801
802 /// Get the variable for the given level
803 ///
804 /// Panics if `level >= self.num_levels()`.
805 fn level_to_var(&self, level: LevelNo) -> VarNo;
806
807 /// Get the level given by `no`
808 ///
809 /// Implementations may or may not acquire a lock here.
810 ///
811 /// Panics if `no >= self.num_levels()`.
812 #[must_use]
813 fn level(&self, no: LevelNo) -> Self::LevelView<'_>;
814
815 /// Get the level given by `no`. Unsafe version of [`Self::level()`]
816 ///
817 /// Implementations may or may not acquire a lock here.
818 ///
819 /// # Safety
820 ///
821 /// `no < self.num_levels()` must hold.
822 #[must_use]
823 unsafe fn level_unchecked(&self, no: LevelNo) -> Self::LevelView<'_>;
824
825 /// Iterate over the levels from top to bottom
826 #[must_use]
827 fn levels(&self) -> Self::LevelIterator<'_>;
828
829 /// Get an edge for the given terminal
830 ///
831 /// Locking behavior: May acquire a lock for the internal terminal unique
832 /// table. In particular, this means that calling this function while
833 /// holding a terminal iterator ([`Manager::terminals()`]) may cause a
834 /// deadlock.
835 #[must_use]
836 fn get_terminal(&self, terminal: Self::Terminal) -> AllocResult<Self::Edge>;
837
838 /// Get the number of terminals
839 ///
840 /// Should agree with the length of the iterator returned by
841 /// [`Manager::terminals()`].
842 ///
843 /// Locking behavior: May acquire a lock for the internal terminal unique
844 /// table. In particular, this means that calling this function while
845 /// holding a terminal iterator ([`Manager::terminals()`]) may cause a
846 /// deadlock.
847 #[must_use]
848 fn num_terminals(&self) -> usize;
849
850 /// Iterator over all terminals
851 ///
852 /// Locking behavior: May acquire a lock for the internal terminal unique
853 /// table.
854 #[must_use]
855 fn terminals(&self) -> Self::TerminalIterator<'_>;
856
857 /// Perform garbage collection
858 ///
859 /// This method looks for nodes that are neither referenced by a
860 /// [`Function`][function::Function] nor another node and removes them. The
861 /// method works from top to bottom, so if a node is only referenced by
862 /// nodes that can be removed, this node will be removed as well.
863 ///
864 /// Returns the number of nodes removed.
865 ///
866 /// The implementation should emit the [`ManagerEventSubscriber::pre_gc()`]
867 /// and [`ManagerEventSubscriber::post_gc()`] events unless called from the
868 /// closure passed to [`Self::reorder()`].
869 fn gc(&self) -> usize;
870
871 /// Prepare and postprocess a reordering operation. The reordering itself is
872 /// performed in `f`.
873 ///
874 /// Returns the value returned by `f`.
875 ///
876 /// In case of a recursive call (i.e., the closure passed to this method
877 /// calls this method again), the implementation should just call `f` and
878 /// return.
879 ///
880 /// Otherwise, the implementation should emit
881 /// [events][ManagerEventSubscriber] in the following order:
882 ///
883 /// 1. [`pre_gc`][ManagerEventSubscriber::pre_gc()]. This enables node
884 /// removal during the `pre_reorder` events.
885 /// 2. [`pre_reorder`][ManagerEventSubscriber::pre_reorder()] and
886 /// [`pre_reorder_mut`][ManagerEventSubscriber::pre_reorder_mut()]
887 /// 3. Call `f`
888 /// 4. [`post_reorder`][ManagerEventSubscriber::post_reorder()] and
889 /// [`post_reorder_mut`][ManagerEventSubscriber::post_reorder_mut()]
890 /// 5. [`post_gc`][ManagerEventSubscriber::post_gc()]
891 fn reorder<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T;
892
893 /// Get the count of garbage collections
894 ///
895 /// This counter should monotonically increase to ensure that caches are
896 /// invalidated accordingly.
897 fn gc_count(&self) -> u64;
898
899 /// Get the count of reordering operations
900 ///
901 /// This counter should monotonically increase to ensure that caches are
902 /// invalidated accordingly.
903 fn reorder_count(&self) -> u64;
904}
905
906/// Event subscriber for [`Manager`]-related events
907///
908/// This is intended to be implemented by data structures that can be plugged
909/// into the [`Manager`], e.g., an [`ApplyCache`] implementation.
910///
911/// # Use Cases
912///
913/// When using reference counting to implement garbage collection of dead nodes,
914/// cloning and dropping edges when inserting entries into the apply cache may
915/// cause many CPU cache misses. To circumvent this performance issue, the apply
916/// cache may store [`Borrowed<M::Edge>`]s (e.g., using the unsafe
917/// [`Borrowed::into_inner()`]). Now, the apply cache implementation has to
918/// guarantee that every edge returned by the [`get()`][ApplyCache::get]
919/// method still points to a valid node. To that end, the cache may, e.g., clear
920/// itself when [`Self::pre_gc()`] is called and reject any insertion of new
921/// entries until [`Self::post_gc()`].
922///
923/// # Safety
924///
925/// A use-case such as the one described above requires the `pre_*` methods to
926/// actually be called before any garbage collection / reordering for SAFETY.
927/// This means the struct implementing `ManagerEventSubscriber` must only be
928/// plugged into [`Manager`] implementations or other wrapper structures that
929/// uphold this contract, i.e., creation of the `ManagerEventSubscriber`
930/// implementation must be `unsafe`.
931///
932/// Additionally, we require each [`post_gc`][Self::post_gc] call to be paired
933/// with a distinct preceding [`pre_gc`][Self::pre_gc] call, see below.
934pub trait ManagerEventSubscriber<M: Manager> {
935 /// Initialization
936 ///
937 /// This method is called once at the very end of initializing a new
938 /// manager.
939 #[inline(always)]
940 #[allow(unused_variables)]
941 fn init(&self, manager: &M) {}
942
943 /// Initialization
944 ///
945 /// This is an alternative to [`Self::post_reorder()`] with a mutable
946 /// reference to the manager as an argument. Since `self` may be a
947 /// substructure of `manager`, the method cannot provide mutable references
948 /// to both `self` and `manager`.
949 #[inline(always)]
950 #[allow(unused_variables)]
951 fn init_mut(manager: &mut M) {}
952
953 /// Prepare a garbage collection
954 ///
955 /// The [`Manager`] implementation should only remove nodes (as part of a
956 /// garbage collection or reordering) after calling this method. Between a
957 /// `pre_gc` and the subsequent [`post_gc`][Self::post_gc] call, there
958 /// should not be another `pre_gc` call.
959 ///
960 /// This method may lock (parts of) `self`. Unlocking is then done in
961 /// [`Self::post_gc()`].
962 ///
963 /// Note that this method and [`Self::pre_reorder`] may both be called in
964 /// case of reordering, but can also be
965 #[inline(always)]
966 #[allow(unused_variables)]
967 fn pre_gc(&self, manager: &M) {}
968
969 /// Post-process a garbage collection
970 ///
971 /// # Safety
972 ///
973 /// Each call to this method must be paired with a distinct preceding
974 /// [`Self::pre_gc()`] call. All operations potentially removing nodes must
975 /// happen between such a pair of method calls.
976 #[inline(always)]
977 #[allow(unused_variables)]
978 unsafe fn post_gc(&self, manager: &M) {}
979
980 /// Prepare a reordering operation (including any addition of levels)
981 ///
982 /// The [`Manager`] implementation should only add or reorder levels after
983 /// calling this method and [`pre_reorder_mut`][Self::pre_reorder_mut].
984 /// Between a `pre_reorder` and the subsequent
985 /// [`post_reorder`][Self::post_reorder] call, there should not be
986 /// another `pre_reorder` call.
987 #[inline(always)]
988 #[allow(unused_variables)]
989 fn pre_reorder(&self, manager: &M) {}
990
991 /// Prepare a reordering operation (including any addition of levels)
992 ///
993 /// This is an alternative to [`Self::pre_reorder()`] with a mutable
994 /// reference to the manager as an argument. Since `self` may be a
995 /// substructure of `manager`, the method cannot provide mutable references
996 /// to both `self` and `manager`.
997 #[inline(always)]
998 #[allow(unused_variables)]
999 fn pre_reorder_mut(manager: &mut M) {}
1000
1001 /// Post-process a reordering operation
1002 ///
1003 /// Each call to this method should be paired with a distinct preceding
1004 /// [`Self::pre_reorder()`] call. All operations reordering (or adding)
1005 /// levels of the decision diagram should happen between such a pair of
1006 /// method calls.
1007 #[inline(always)]
1008 #[allow(unused_variables)]
1009 fn post_reorder(&self, manager: &M) {}
1010
1011 /// Post-process a reordering operation
1012 ///
1013 /// This is an alternative to [`Self::post_reorder()`] with a mutable
1014 /// reference to the manager as an argument. Since `self` may be a
1015 /// substructure of `manager`, the method cannot provide mutable references
1016 /// to both `self` and `manager`.
1017 #[inline(always)]
1018 #[allow(unused_variables)]
1019 fn post_reorder_mut(manager: &mut M) {}
1020}
1021
1022/// View of a single level in the manager
1023///
1024/// # Safety
1025///
1026/// Each level view is associated with a [`Manager`] M and a [`LevelNo`] L. The
1027/// level view must ensure that all contained nodes and their descendants are
1028/// stored in M. The edges returned by [`LevelView::get()`] and
1029/// [`LevelView::get_or_insert()`] must reference nodes at this level.
1030/// [`LevelView::iter()`] must yield all edges to nodes at this level (it must
1031/// not hide some away).
1032///
1033/// [`LevelView::swap()`] conceptually removes all nodes from this level and
1034/// inserts them at the other level and vice versa.
1035pub unsafe trait LevelView<E: Edge, N: InnerNode<E>> {
1036 /// Iterator over [`Edge`]s pointing to nodes at this level
1037 type Iterator<'a>: Iterator<Item = &'a E>
1038 where
1039 Self: 'a,
1040 E: 'a;
1041
1042 /// Taken level view, see [`LevelView::take()`]
1043 type Taken: LevelView<E, N>;
1044
1045 /// Get the number of nodes on this level
1046 #[must_use]
1047 fn len(&self) -> usize;
1048
1049 /// Returns `true` iff this level contains nodes
1050 #[must_use]
1051 fn is_empty(&self) -> bool {
1052 self.len() == 0
1053 }
1054
1055 /// Get the level number of this level
1056 #[must_use]
1057 fn level_no(&self) -> LevelNo;
1058
1059 /// Reserve space for `additional` nodes on this level
1060 fn reserve(&mut self, additional: usize);
1061
1062 /// Get the edge corresponding to the given node (if present)
1063 #[must_use]
1064 fn get(&self, node: &N) -> Option<&E>;
1065
1066 /// Insert the given edge into the unique table at this level, assuming that
1067 /// the referenced node is already stored in the associated manager.
1068 ///
1069 /// Returns `true` if the edge was inserted, `false` if it was already
1070 /// present.
1071 ///
1072 /// Panics if `edge`
1073 /// - points to a terminal node,
1074 /// - references a node from a different manager, or
1075 /// - `N` implements [`HasLevel`] and `edge` references a node for which
1076 /// [`HasLevel::level()`] returns a different level.
1077 ///
1078 /// Furthermore, this function ideally panics if `edge` is tagged, but the
1079 /// caller must not rely on that. An implementation may simply remove the
1080 /// tag for optimization purposes.
1081 fn insert(&mut self, edge: E) -> bool;
1082
1083 /// Insert the given edge into the unique table at this level, assuming that
1084 /// the referenced node is already stored in the associated manager. Unsafe
1085 /// version of [`Self::insert()`].
1086 ///
1087 /// Returns `true` if the edge was inserted, `false` if it was already
1088 /// present.
1089 ///
1090 /// Panics if `edge` references a node from a different manager.
1091 /// Furthermore, this function ideally panics if `edge` is tagged, but the
1092 /// caller must not rely on that. An implementation may simply remove the
1093 /// tag for optimization purposes.
1094 ///
1095 /// # Safety
1096 ///
1097 /// The caller must ensure that edge points to an inner node. Additionally,
1098 /// if `N` implements [`HasLevel`], [`HasLevel::level()`] must return the
1099 /// level number of this level for the referenced node. During reordering,
1100 /// the second requirement does not need to be fulfilled immediately,
1101 /// but must hold at the end of the reordering process.
1102 unsafe fn insert_unchecked(&mut self, edge: E) -> bool;
1103
1104 /// Get the edge corresponding to `level` and `node` if present, or insert
1105 /// it.
1106 ///
1107 /// Panics if
1108 /// - the children of `node` are stored in a different manager, or
1109 /// - `N` implements [`HasLevel`] and
1110 /// [`HasLevel::level(node)`][HasLevel::level()] returns a different
1111 /// level.
1112 #[must_use]
1113 fn get_or_insert(&mut self, node: N) -> AllocResult<E>;
1114
1115 /// Get the edge corresponding to `level` and `node` if present, or insert
1116 /// it. Unsafe version of [`Self::get_or_insert()`]
1117 ///
1118 /// Panics if the children of `node` are stored in a different manager.
1119 ///
1120 /// # Safety
1121 ///
1122 /// If `N` implements [`HasLevel`], the caller must ensure that
1123 /// [`HasLevel::level(node)`][HasLevel::level()] returns the level number of
1124 /// this level. During reordering, this requirement does not need to be
1125 /// fulfilled immediately, but must hold at the end of the reordering
1126 /// process.
1127 #[must_use]
1128 unsafe fn get_or_insert_unchecked(&mut self, node: N) -> AllocResult<E>;
1129
1130 /// Perform garbage collection on this level
1131 ///
1132 /// This method may be a no-op unless a garbage collection has been
1133 /// prepared. For instance, this is what [`Manager::reorder()`] does.
1134 fn gc(&mut self);
1135
1136 /// Remove `node` from (this level of) the manager
1137 ///
1138 /// Returns whether the node was present at this level and has been removed.
1139 ///
1140 /// This method may be a no-op unless a garbage collection has been
1141 /// prepared. For instance, this is what [`Manager::reorder()`] does. In the
1142 /// no-op case, the return value is always false.
1143 fn remove(&mut self, node: &N) -> bool;
1144
1145 /// Move all nodes from this level to the other level and vice versa.
1146 ///
1147 /// # Safety
1148 ///
1149 /// This method does not necessarily change the level returned by
1150 /// [`HasLevel::level()`] for the nodes at this or the `other` level. The
1151 /// caller must ensure a consistent state using calls to
1152 /// [`HasLevel::set_level()`]. (Be aware of exception safety!)
1153 unsafe fn swap(&mut self, other: &mut Self);
1154
1155 /// Iterate over all the edges at this level
1156 #[must_use]
1157 fn iter(&self) -> Self::Iterator<'_>;
1158
1159 /// If a reordering operation is in progress: Clear this level, returning a
1160 /// level view containing all the previous edges. Otherwise, this method
1161 /// returns `None` and leaves `self` unchanged.
1162 #[must_use]
1163 fn take(&mut self) -> Option<Self::Taken>;
1164}
1165
1166/// Cache for the result of apply operations
1167///
1168/// This trait provides methods to add computation results to the apply cache
1169/// and to query the cache for these results. Just as every cache, the
1170/// implementation may decide to evict results from the cache.
1171pub trait ApplyCache<M: Manager, O: Copy>: DropWith<M::Edge> {
1172 /// Get the result for the key consisting of `operand` and `operator`, if
1173 /// cached
1174 ///
1175 /// This is the extended version of [`Self::get()`], allowing for numeric
1176 /// operands and multiple, possibly numeric values. The constants `E` and
1177 /// `N` indicate the number of expected edge values and numeric values,
1178 /// respectively.
1179 #[must_use]
1180 fn get_extended<const E: usize, const N: usize>(
1181 &self,
1182 manager: &M,
1183 operator: O,
1184 operands: (&[Borrowed<M::Edge>], &[u32]),
1185 ) -> Option<([M::Edge; E], [u32; N])>;
1186
1187 /// Add the result of `operation` to this cache
1188 ///
1189 /// An implementation is free to not cache any result. (This is why we use
1190 /// `Borrowed<M::Edge>`, which in this case elides a few clone and drop
1191 /// operations.) If the cache already contains the key consisting of
1192 /// `operator` and `operands`, there is no need to update its value. (Again,
1193 /// an implementation could elide clone and drop operations.)
1194 fn add_extended(
1195 &self,
1196 manager: &M,
1197 operator: O,
1198 operands: (&[Borrowed<M::Edge>], &[u32]),
1199 values: (&[Borrowed<M::Edge>], &[u32]),
1200 );
1201
1202 /// Shorthand for [`Self::get_extended()`] without numeric operands and just
1203 /// a single edge value
1204 #[inline(always)]
1205 #[must_use]
1206 fn get(&self, manager: &M, operator: O, operands: &[Borrowed<M::Edge>]) -> Option<M::Edge> {
1207 let ([e], []) = self.get_extended::<1, 0>(manager, operator, (operands, &[]))?;
1208 Some(e)
1209 }
1210
1211 /// Shorthand for [`Self::add_extended()`] without numeric operands and just
1212 /// a single edge value
1213 #[inline(always)]
1214 fn add(
1215 &self,
1216 manager: &M,
1217 operator: O,
1218 operands: &[Borrowed<M::Edge>],
1219 value: Borrowed<M::Edge>,
1220 ) {
1221 self.add_extended(manager, operator, (operands, &[]), (&[value], &[]))
1222 }
1223
1224 /// Remove all entries from the cache
1225 fn clear(&self, manager: &M);
1226}
1227
1228/// Apply cache container
1229///
1230/// Intended to be implemented by [`Manager`]s such that generic implementations
1231/// of the recursive apply algorithm can simply require
1232/// `M: Manager + HasApplyCache<M, O>`, where `O` is the operator type.
1233pub trait HasApplyCache<M: Manager, O: Copy> {
1234 /// The concrete apply cache type
1235 type ApplyCache: ApplyCache<M, O>;
1236
1237 /// Get a shared reference to the contained apply cache
1238 #[must_use]
1239 fn apply_cache(&self) -> &Self::ApplyCache;
1240
1241 /// Get a mutable reference to the contained apply cache
1242 #[must_use]
1243 fn apply_cache_mut(&mut self) -> &mut Self::ApplyCache;
1244}
1245
1246/// Worker thread pool associated with a [`Manager`]
1247///
1248/// A manager having its own thread pool has the advantage that it may use
1249/// thread-local storage for its workers to pre-allocate some resources (e.g.,
1250/// slots for nodes) and thereby reduce lock contention.
1251pub trait WorkerPool: Sync {
1252 /// Get the current number of threads
1253 fn current_num_threads(&self) -> usize;
1254
1255 /// Get the recursion depth up to which operations are split
1256 fn split_depth(&self) -> u32;
1257
1258 /// Set the recursion depth up to which operations are split
1259 ///
1260 /// `None` means that the implementation should automatically choose the
1261 /// depth. `Some(0)` means that no operations are split.
1262 fn set_split_depth(&self, depth: Option<u32>);
1263
1264 /// Execute `op` within the thread pool
1265 ///
1266 /// If this method is called from another thread pool, it may cooperatively
1267 /// yield execution to that pool until `op` has finished.
1268 fn install<R: Send>(&self, op: impl FnOnce() -> R + Send) -> R;
1269
1270 /// Execute `op_a` and `op_b` in parallel within the thread pool
1271 ///
1272 /// Note that the split depth has no influence on this method. Checking
1273 /// whether to split an operation must be done externally.
1274 fn join<RA: Send, RB: Send>(
1275 &self,
1276 op_a: impl FnOnce() -> RA + Send,
1277 op_b: impl FnOnce() -> RB + Send,
1278 ) -> (RA, RB);
1279
1280 /// Execute `op` on every worker in the thread pool
1281 fn broadcast<R: Send>(&self, op: impl Fn(BroadcastContext) -> R + Sync) -> Vec<R>;
1282
1283 /// Execute `op` concurrently for every element of `slice`
1284 fn slice_for_each<T: Sync>(&self, slice: &[T], op: impl Fn(&T) + Sync) {
1285 match slice {
1286 [] => {}
1287 [x] => op(x),
1288 _ => {
1289 let done = std::sync::atomic::AtomicU64::new(0);
1290 self.broadcast(|_| {
1291 loop {
1292 let i = done.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1293 if i >= slice.len() as u64 {
1294 return;
1295 }
1296 op(&slice[i as usize])
1297 }
1298 });
1299 }
1300 }
1301 }
1302}
1303
1304/// Context provided to workers by [`WorkerPool::broadcast()`]
1305#[derive(Clone, Copy, Debug)]
1306pub struct BroadcastContext {
1307 /// Index of this worker (in range `0..num_threads`)
1308 pub index: u32,
1309
1310 /// Number of threads receiving the broadcast
1311 pub num_threads: u32,
1312}
1313
1314/// Helper trait to be implemented by [`Manager`] and [`ManagerRef`] if they
1315/// feature a [`WorkerPool`].
1316pub trait HasWorkers: Sync {
1317 /// Type of the worker pool
1318 type WorkerPool: WorkerPool;
1319
1320 /// Get the worker pool
1321 fn workers(&self) -> &Self::WorkerPool;
1322}