Skip to content

Commit f52e19c

Browse files
authored
Replace public NodeIndex with generic NodeId (#2)
* Make node value of ModuleKind generic. Remove NodeIndex from public interface * Add tests
1 parent e2efc67 commit f52e19c

File tree

5 files changed

+140
-36
lines changed

5 files changed

+140
-36
lines changed

crates/fracture/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ mod base;
33
use common::modular_decomposition::MDNodeKind;
44
use modular_decomposition::fracture::modular_decomposition as fracture_modular_decomposition;
55
use modular_decomposition::ModuleKind;
6-
use petgraph::graph::{DiGraph, UnGraph};
6+
use petgraph::graph::{DiGraph, NodeIndex, UnGraph};
77
use tracing::info;
88

99
#[macro_export]
@@ -28,7 +28,7 @@ pub fn prepare<N, E>(graph: &UnGraph<N, E>) -> Prepared {
2828
}
2929

3030
pub struct Computed {
31-
tree: DiGraph<ModuleKind, ()>,
31+
tree: DiGraph<ModuleKind<NodeIndex>, ()>,
3232
}
3333

3434
impl Prepared {

crates/modular-decomposition/examples/adj_vector_graph.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,6 @@ fn main() {
8282
factorizing_permutation.push(u);
8383
}
8484
}
85-
let factorizing_permutation: Vec<_> = factorizing_permutation.iter().map(|u| u.index()).collect();
85+
let factorizing_permutation: Vec<_> = factorizing_permutation.to_vec();
8686
println!("{:?}", factorizing_permutation);
8787
}

crates/modular-decomposition/src/fracture/mod.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use tracing::{info, instrument};
1414
///
1515
/// Returns a `NullGraphError` if the input graph does not contain any nodes or edges.
1616
#[instrument(skip_all)]
17-
pub fn modular_decomposition<G>(graph: G) -> Result<MDTree, NullGraphError>
17+
pub fn modular_decomposition<G>(graph: G) -> Result<MDTree<G::NodeId>, NullGraphError>
1818
where
1919
G: NodeCompactIndexable + IntoNeighbors + GraphProp<EdgeType = Undirected>,
2020
{
@@ -24,7 +24,7 @@ where
2424
}
2525
if n == 1 {
2626
let mut tree = DiGraph::new();
27-
tree.add_node(ModuleKind::Node(NodeIndex::new(0)));
27+
tree.add_node(ModuleKind::Node(graph.from_index(0)));
2828
return MDTree::from_digraph(tree);
2929
}
3030

@@ -257,7 +257,7 @@ pub(crate) fn create_consecutive_twin_nodes(op: &mut [u32], cl: &mut [u32], lc:
257257
}
258258

259259
#[instrument(skip_all)]
260-
pub(crate) fn build_tree<G>(graph: G, op: &[u32], cl: &[u32], p: &Permutation) -> DiGraph<ModuleKind, ()>
260+
pub(crate) fn build_tree<G>(graph: G, op: &[u32], cl: &[u32], p: &Permutation) -> DiGraph<ModuleKind<G::NodeId>, ()>
261261
where
262262
G: NodeCompactIndexable + IntoNeighbors + GraphProp<EdgeType = Undirected>,
263263
{
@@ -269,16 +269,17 @@ where
269269
let degrees: Vec<u32> =
270270
(0..graph.node_bound()).map(|i| graph.from_index(i)).map(|u| graph.neighbors(u).count() as _).collect();
271271

272-
let handle_vertex_node = |t: &mut DiGraph<ModuleKind, ()>,
273-
x: NodeIndex|
274-
-> (NodeIndex, petgraph::graph::NodeIndex) { (x, t.add_node(ModuleKind::Node(x))) };
272+
let handle_vertex_node =
273+
|t: &mut DiGraph<ModuleKind<G::NodeId>, ()>, x: NodeIndex| -> (NodeIndex, petgraph::graph::NodeIndex) {
274+
(x, t.add_node(ModuleKind::Node(graph.from_index(x.index()))))
275+
};
275276

276277
let mut marked = vec![0; n];
277278
let mut gen = 0;
278279

279-
let mut determine_node_kind = |t: &mut DiGraph<ModuleKind, ()>,
280+
let mut determine_node_kind = |t: &mut DiGraph<ModuleKind<G::NodeId>, ()>,
280281
nodes: &[(NodeIndex, petgraph::graph::NodeIndex)]|
281-
-> (NodeIndex, ModuleKind) {
282+
-> (NodeIndex, ModuleKind<G::NodeId>) {
282283
// Calculate the degrees between children of a module.
283284
// Every module keeps a graph node as a representative. Mark the representatives
284285
// of the children. For each representative, iterate over its neighbors.
@@ -330,13 +331,13 @@ where
330331

331332
if kind != ModuleKind::Prime {
332333
debug_assert!(nodes.iter().map(|(y, _)| quotient_degree(*y)).all(|d| d == d0));
333-
debug_assert!(nodes.iter().all(|(_, u)| t[*u] != kind), "{:?}", kind);
334+
debug_assert!(nodes.iter().all(|(_, u)| t[*u] != kind));
334335
}
335336

336337
(y, kind)
337338
};
338339

339-
let mut handle_inner_node = |t: &mut DiGraph<ModuleKind, ()>,
340+
let mut handle_inner_node = |t: &mut DiGraph<ModuleKind<G::NodeId>, ()>,
340341
nodes: &[(NodeIndex, petgraph::graph::NodeIndex)]|
341342
-> (NodeIndex, petgraph::graph::NodeIndex) {
342343
if nodes.len() == 1 {

crates/modular-decomposition/src/lib.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,15 @@ mod tests;
6969

7070
pub use fracture::modular_decomposition;
7171
pub use md_tree::MDTree;
72-
pub use md_tree::ModuleKind;
7372
pub use md_tree::ModuleIndex;
73+
pub use md_tree::ModuleKind;
7474

7575
#[cfg(test)]
7676
mod test {
7777
use super::*;
78-
use crate::md_tree::{MDTree, ModuleKind, NodeIndex};
78+
use crate::md_tree::{MDTree, ModuleKind};
79+
use petgraph::graph::NodeIndex;
80+
use petgraph::visit::NodeIndexable;
7981

8082
#[derive(Default, Debug)]
8183
struct ModuleKindCounts {
@@ -91,7 +93,7 @@ mod test {
9193
}
9294
}
9395

94-
fn count_module_kinds(md: &MDTree) -> ModuleKindCounts {
96+
fn count_module_kinds(md: &MDTree<NodeIndex>) -> ModuleKindCounts {
9597
let mut counts = ModuleKindCounts::default();
9698
for kind in md.module_kinds() {
9799
match kind {
@@ -117,7 +119,7 @@ mod test {
117119
let md = modular_decomposition(&graph).unwrap();
118120
assert_eq!(md.node_count(), 1);
119121
assert_eq!(count_module_kinds(&md), [0, 0, 0, 1]);
120-
assert_eq!(md.module_kind(md.root()), Some(&ModuleKind::Node(NodeIndex::new(0))));
122+
assert_eq!(md.module_kind(md.root()), Some(&ModuleKind::Node(graph.from_index(0))));
121123
}
122124

123125
#[test]
Lines changed: 120 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
use crate::index::make_index;
1+
use std::fmt::{Debug, Display, Formatter};
2+
23
use petgraph::graph::DiGraph;
34
use petgraph::{Incoming, Outgoing};
4-
use std::fmt::{Debug, Display, Formatter};
55

6-
make_index!(pub NodeIndex);
6+
use crate::index::make_index;
7+
8+
make_index!(pub(crate) NodeIndex);
79

810
/// Module kinds of nodes in a [MDTree].
911
///
@@ -13,18 +15,18 @@ make_index!(pub NodeIndex);
1315
/// The module kinds are determined by the quotient graph of a module that is obtained by taking a
1416
/// single node from each child module.
1517
#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
16-
pub enum ModuleKind {
18+
pub enum ModuleKind<NodeId: Copy + PartialEq> {
1719
/// A prime module. Its quotient graph has only trivial modules.
1820
Prime,
1921
/// A series module. Its quotient graph is a complete graph.
2022
Series,
2123
/// A parallel module. Its quotient graph is an empty graph.
2224
Parallel,
2325
/// A trivial module with a single vertex. This is leaf node in the [MDTree].
24-
Node(NodeIndex),
26+
Node(NodeId),
2527
}
2628

27-
impl Debug for ModuleKind {
29+
impl<NodeId: Debug + Copy + PartialEq> Debug for ModuleKind<NodeId> {
2830
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2931
match self {
3032
ModuleKind::Prime => {
@@ -37,24 +39,42 @@ impl Debug for ModuleKind {
3739
write!(f, "Parallel")
3840
}
3941
ModuleKind::Node(v) => {
40-
write!(f, "{v}")
42+
write!(f, "{v:?}")
4143
}
4244
}
4345
}
4446
}
4547

4648
/// A modular decomposition tree. The tree contains at least one node.
4749
#[derive(Clone, Debug)]
48-
pub struct MDTree {
49-
tree: DiGraph<ModuleKind, ()>,
50-
root: petgraph::graph::NodeIndex,
50+
pub struct MDTree<NodeId: Copy + PartialEq> {
51+
tree: DiGraph<ModuleKind<NodeId>, ()>,
52+
root: ModuleIndex,
5153
}
5254

5355
/// Module identifier.
54-
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
55-
pub struct ModuleIndex(petgraph::graph::NodeIndex);
56+
#[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
57+
pub struct ModuleIndex(pub(crate) petgraph::graph::NodeIndex);
5658

57-
impl MDTree {
59+
impl Debug for ModuleIndex {
60+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
61+
f.debug_tuple("ModuleIndex").field(&self.0.index()).finish()
62+
}
63+
}
64+
65+
impl ModuleIndex {
66+
/// Create new index from `usize`.
67+
pub fn new(x: usize) -> Self {
68+
Self(petgraph::graph::NodeIndex::new(x))
69+
}
70+
71+
/// Returns the index as `usize`.
72+
pub fn index(&self) -> usize {
73+
self.0.index()
74+
}
75+
}
76+
77+
impl<NodeId: Copy + PartialEq> MDTree<NodeId> {
5878
/// Create a new modular decomposition tree.
5979
///
6080
/// Assumes that the input `DiGraph` is rooted tree with node weights
@@ -64,11 +84,12 @@ impl MDTree {
6484
/// Return `NullGraph` if the input graph does not have any nodes.
6585
///
6686
/// Panics if all nodes have a non-zero in-degree.
67-
pub(crate) fn from_digraph(tree: DiGraph<ModuleKind, ()>) -> Result<Self, NullGraphError> {
87+
pub(crate) fn from_digraph(tree: DiGraph<ModuleKind<NodeId>, ()>) -> Result<Self, NullGraphError> {
6888
if tree.node_count() == 0 {
6989
return Err(NullGraphError);
7090
}
7191
let root = tree.externals(Incoming).next().expect("non-null trees must have a root");
92+
let root = ModuleIndex(root);
7293
Ok(Self { tree, root })
7394
}
7495

@@ -81,18 +102,18 @@ impl MDTree {
81102
/// Return the root node index.
82103
#[inline(always)]
83104
pub fn root(&self) -> ModuleIndex {
84-
ModuleIndex(self.root)
105+
self.root
85106
}
86107

87108
/// Access the [ModuleKind] of a module.
88109
///
89110
/// If the module does not exist, return None.
90-
pub fn module_kind(&self, module: ModuleIndex) -> Option<&ModuleKind> {
111+
pub fn module_kind(&self, module: ModuleIndex) -> Option<&ModuleKind<NodeId>> {
91112
self.tree.node_weight(module.0)
92113
}
93114

94115
/// Return an iterator yielding references to [ModuleKind]s for all nodes.
95-
pub fn module_kinds(&self) -> impl Iterator<Item = &ModuleKind> {
116+
pub fn module_kinds(&self) -> impl Iterator<Item = &ModuleKind<NodeId>> {
96117
self.tree.node_weights()
97118
}
98119

@@ -102,13 +123,50 @@ impl MDTree {
102123
}
103124

104125
/// Convert to [DiGraph].
105-
pub fn into_digraph(self) -> DiGraph<ModuleKind, ()> {
126+
///
127+
/// This allows the use of [petgraph] algorithms. Use [ModuleIndex::index] and
128+
/// [petgraph::graph::NodeIndex::new] to convert the root index.
129+
///
130+
/// ```rust
131+
/// # use std::error::Error;
132+
/// #
133+
/// # fn main() -> Result<(), Box<dyn Error>> {
134+
/// use petgraph::graph::{NodeIndex, UnGraph};
135+
/// use petgraph::visit::Dfs;
136+
/// use modular_decomposition::{modular_decomposition, ModuleKind};
137+
///
138+
/// let graph = UnGraph::<(), ()>::from_edges([(0, 2), (1, 2), (2, 3), (3, 4), (3, 5)]);
139+
/// let md = modular_decomposition(&graph)?;
140+
///
141+
/// let root = NodeIndex::new(md.root().index());
142+
/// let digraph = md.into_digraph();
143+
///
144+
/// let mut dfs = Dfs::new(&digraph, root);
145+
/// let mut node_order = vec![];
146+
/// while let Some(node) = dfs.next(&digraph) { node_order.push(*digraph.node_weight(node).unwrap()); }
147+
///
148+
/// let expected_node_order = [
149+
/// ModuleKind::Prime,
150+
/// ModuleKind::Node(NodeIndex::new(2)),
151+
/// ModuleKind::Parallel,
152+
/// ModuleKind::Node(NodeIndex::new(0)),
153+
/// ModuleKind::Node(NodeIndex::new(1)),
154+
/// ModuleKind::Node(NodeIndex::new(3)),
155+
/// ModuleKind::Parallel,
156+
/// ModuleKind::Node(NodeIndex::new(4)),
157+
/// ModuleKind::Node(NodeIndex::new(5)),
158+
/// ];
159+
/// assert_eq!(node_order, expected_node_order);
160+
/// # Ok(())
161+
/// # }
162+
/// ```
163+
pub fn into_digraph(self) -> DiGraph<ModuleKind<NodeId>, ()> {
106164
self.tree
107165
}
108166
}
109167

110168
/// A graph does not contain any nodes or edges.
111-
#[derive(Copy, Clone, Debug)]
169+
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
112170
pub struct NullGraphError;
113171

114172
impl Display for NullGraphError {
@@ -118,3 +176,46 @@ impl Display for NullGraphError {
118176
}
119177

120178
impl std::error::Error for NullGraphError {}
179+
180+
#[cfg(test)]
181+
mod test {
182+
use petgraph::graph::{DiGraph, NodeIndex};
183+
use petgraph::Outgoing;
184+
185+
use crate::md_tree::NullGraphError;
186+
use crate::tests::complete_graph;
187+
use crate::{modular_decomposition, MDTree, ModuleIndex, ModuleKind};
188+
189+
#[test]
190+
fn mdtree_and_digraph_are_equivalent() {
191+
let graph = complete_graph(5);
192+
let md = modular_decomposition(&graph).unwrap();
193+
let root = md.root();
194+
195+
assert_eq!(md.module_kind(root), Some(&ModuleKind::Series));
196+
197+
let children: Vec<_> = md.children(root).collect();
198+
assert_eq!(md.module_kind(children[0]), Some(&ModuleKind::Node(NodeIndex::new(0))));
199+
200+
let md = md.into_digraph();
201+
let root = NodeIndex::new(root.index());
202+
203+
let children: Vec<_> = md.neighbors_directed(root, Outgoing).collect();
204+
assert_eq!(md.node_weight(root), Some(&ModuleKind::Series));
205+
assert_eq!(md.node_weight(children[0]), Some(&ModuleKind::Node(NodeIndex::new(0))));
206+
}
207+
208+
#[test]
209+
fn null_graph_error() {
210+
let digraph: DiGraph<ModuleKind<NodeIndex>, ()> = Default::default();
211+
let err = MDTree::from_digraph(digraph).unwrap_err();
212+
assert_eq!(err, NullGraphError);
213+
assert_eq!(format!("{}", err), "graph does not contain any nodes or edges".to_string());
214+
}
215+
216+
#[test]
217+
fn module_index_fmt() {
218+
let idx = ModuleIndex::new(42);
219+
assert_eq!(format!("{:?}", idx), "ModuleIndex(42)".to_string())
220+
}
221+
}

0 commit comments

Comments
 (0)