Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace public NodeIndex with generic NodeId #2

Merged
merged 5 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/fracture/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ mod base;
use common::modular_decomposition::MDNodeKind;
use modular_decomposition::fracture::modular_decomposition as fracture_modular_decomposition;
use modular_decomposition::ModuleKind;
use petgraph::graph::{DiGraph, UnGraph};
use petgraph::graph::{DiGraph, NodeIndex, UnGraph};
use tracing::info;

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

pub struct Computed {
tree: DiGraph<ModuleKind, ()>,
tree: DiGraph<ModuleKind<NodeIndex>, ()>,
}

impl Prepared {
Expand Down
2 changes: 1 addition & 1 deletion crates/modular-decomposition/examples/adj_vector_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,6 @@ fn main() {
factorizing_permutation.push(u);
}
}
let factorizing_permutation: Vec<_> = factorizing_permutation.iter().map(|u| u.index()).collect();
let factorizing_permutation: Vec<_> = factorizing_permutation.to_vec();
println!("{:?}", factorizing_permutation);
}
21 changes: 11 additions & 10 deletions crates/modular-decomposition/src/fracture/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use tracing::{info, instrument};
///
/// Returns a `NullGraphError` if the input graph does not contain any nodes or edges.
#[instrument(skip_all)]
pub fn modular_decomposition<G>(graph: G) -> Result<MDTree, NullGraphError>
pub fn modular_decomposition<G>(graph: G) -> Result<MDTree<G::NodeId>, NullGraphError>
where
G: NodeCompactIndexable + IntoNeighbors + GraphProp<EdgeType = Undirected>,
{
Expand All @@ -24,7 +24,7 @@ where
}
if n == 1 {
let mut tree = DiGraph::new();
tree.add_node(ModuleKind::Node(NodeIndex::new(0)));
tree.add_node(ModuleKind::Node(graph.from_index(0)));
return MDTree::from_digraph(tree);
}

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

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

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

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

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

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

(y, kind)
};

let mut handle_inner_node = |t: &mut DiGraph<ModuleKind, ()>,
let mut handle_inner_node = |t: &mut DiGraph<ModuleKind<G::NodeId>, ()>,
nodes: &[(NodeIndex, petgraph::graph::NodeIndex)]|
-> (NodeIndex, petgraph::graph::NodeIndex) {
if nodes.len() == 1 {
Expand Down
10 changes: 6 additions & 4 deletions crates/modular-decomposition/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,15 @@ mod tests;

pub use fracture::modular_decomposition;
pub use md_tree::MDTree;
pub use md_tree::ModuleKind;
pub use md_tree::ModuleIndex;
pub use md_tree::ModuleKind;

#[cfg(test)]
mod test {
use super::*;
use crate::md_tree::{MDTree, ModuleKind, NodeIndex};
use crate::md_tree::{MDTree, ModuleKind};
use petgraph::graph::NodeIndex;
use petgraph::visit::NodeIndexable;

#[derive(Default, Debug)]
struct ModuleKindCounts {
Expand All @@ -91,7 +93,7 @@ mod test {
}
}

fn count_module_kinds(md: &MDTree) -> ModuleKindCounts {
fn count_module_kinds(md: &MDTree<NodeIndex>) -> ModuleKindCounts {
let mut counts = ModuleKindCounts::default();
for kind in md.module_kinds() {
match kind {
Expand All @@ -117,7 +119,7 @@ mod test {
let md = modular_decomposition(&graph).unwrap();
assert_eq!(md.node_count(), 1);
assert_eq!(count_module_kinds(&md), [0, 0, 0, 1]);
assert_eq!(md.module_kind(md.root()), Some(&ModuleKind::Node(NodeIndex::new(0))));
assert_eq!(md.module_kind(md.root()), Some(&ModuleKind::Node(graph.from_index(0))));
}

#[test]
Expand Down
139 changes: 120 additions & 19 deletions crates/modular-decomposition/src/md_tree.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use crate::index::make_index;
use std::fmt::{Debug, Display, Formatter};

use petgraph::graph::DiGraph;
use petgraph::{Incoming, Outgoing};
use std::fmt::{Debug, Display, Formatter};

make_index!(pub NodeIndex);
use crate::index::make_index;

make_index!(pub(crate) NodeIndex);

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

impl Debug for ModuleKind {
impl<NodeId: Debug + Copy + PartialEq> Debug for ModuleKind<NodeId> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ModuleKind::Prime => {
Expand All @@ -37,24 +39,42 @@ impl Debug for ModuleKind {
write!(f, "Parallel")
}
ModuleKind::Node(v) => {
write!(f, "{v}")
write!(f, "{v:?}")
}
}
}
}

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

/// Module identifier.
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct ModuleIndex(petgraph::graph::NodeIndex);
#[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct ModuleIndex(pub(crate) petgraph::graph::NodeIndex);

impl MDTree {
impl Debug for ModuleIndex {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("ModuleIndex").field(&self.0.index()).finish()
}
}

impl ModuleIndex {
/// Create new index from `usize`.
pub fn new(x: usize) -> Self {
Self(petgraph::graph::NodeIndex::new(x))
}

/// Returns the index as `usize`.
pub fn index(&self) -> usize {
self.0.index()
}
}

impl<NodeId: Copy + PartialEq> MDTree<NodeId> {
/// Create a new modular decomposition tree.
///
/// Assumes that the input `DiGraph` is rooted tree with node weights
Expand All @@ -64,11 +84,12 @@ impl MDTree {
/// Return `NullGraph` if the input graph does not have any nodes.
///
/// Panics if all nodes have a non-zero in-degree.
pub(crate) fn from_digraph(tree: DiGraph<ModuleKind, ()>) -> Result<Self, NullGraphError> {
pub(crate) fn from_digraph(tree: DiGraph<ModuleKind<NodeId>, ()>) -> Result<Self, NullGraphError> {
if tree.node_count() == 0 {
return Err(NullGraphError);
}
let root = tree.externals(Incoming).next().expect("non-null trees must have a root");
let root = ModuleIndex(root);
Ok(Self { tree, root })
}

Expand All @@ -81,18 +102,18 @@ impl MDTree {
/// Return the root node index.
#[inline(always)]
pub fn root(&self) -> ModuleIndex {
ModuleIndex(self.root)
self.root
}

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

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

Expand All @@ -102,13 +123,50 @@ impl MDTree {
}

/// Convert to [DiGraph].
pub fn into_digraph(self) -> DiGraph<ModuleKind, ()> {
///
/// This allows the use of [petgraph] algorithms. Use [ModuleIndex::index] and
/// [petgraph::graph::NodeIndex::new] to convert the root index.
///
/// ```rust
/// # use std::error::Error;
/// #
/// # fn main() -> Result<(), Box<dyn Error>> {
/// use petgraph::graph::{NodeIndex, UnGraph};
/// use petgraph::visit::Dfs;
/// use modular_decomposition::{modular_decomposition, ModuleKind};
///
/// let graph = UnGraph::<(), ()>::from_edges([(0, 2), (1, 2), (2, 3), (3, 4), (3, 5)]);
/// let md = modular_decomposition(&graph)?;
///
/// let root = NodeIndex::new(md.root().index());
/// let digraph = md.into_digraph();
///
/// let mut dfs = Dfs::new(&digraph, root);
/// let mut node_order = vec![];
/// while let Some(node) = dfs.next(&digraph) { node_order.push(*digraph.node_weight(node).unwrap()); }
///
/// let expected_node_order = [
/// ModuleKind::Prime,
/// ModuleKind::Node(NodeIndex::new(2)),
/// ModuleKind::Parallel,
/// ModuleKind::Node(NodeIndex::new(0)),
/// ModuleKind::Node(NodeIndex::new(1)),
/// ModuleKind::Node(NodeIndex::new(3)),
/// ModuleKind::Parallel,
/// ModuleKind::Node(NodeIndex::new(4)),
/// ModuleKind::Node(NodeIndex::new(5)),
/// ];
/// assert_eq!(node_order, expected_node_order);
/// # Ok(())
/// # }
/// ```
pub fn into_digraph(self) -> DiGraph<ModuleKind<NodeId>, ()> {
self.tree
}
}

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

impl Display for NullGraphError {
Expand All @@ -118,3 +176,46 @@ impl Display for NullGraphError {
}

impl std::error::Error for NullGraphError {}

#[cfg(test)]
mod test {
use petgraph::graph::{DiGraph, NodeIndex};
use petgraph::Outgoing;

use crate::md_tree::NullGraphError;
use crate::tests::complete_graph;
use crate::{modular_decomposition, MDTree, ModuleIndex, ModuleKind};

#[test]
fn mdtree_and_digraph_are_equivalent() {
let graph = complete_graph(5);
let md = modular_decomposition(&graph).unwrap();
let root = md.root();

assert_eq!(md.module_kind(root), Some(&ModuleKind::Series));

let children: Vec<_> = md.children(root).collect();
assert_eq!(md.module_kind(children[0]), Some(&ModuleKind::Node(NodeIndex::new(0))));

let md = md.into_digraph();
let root = NodeIndex::new(root.index());

let children: Vec<_> = md.neighbors_directed(root, Outgoing).collect();
assert_eq!(md.node_weight(root), Some(&ModuleKind::Series));
assert_eq!(md.node_weight(children[0]), Some(&ModuleKind::Node(NodeIndex::new(0))));
}

#[test]
fn null_graph_error() {
let digraph: DiGraph<ModuleKind<NodeIndex>, ()> = Default::default();
let err = MDTree::from_digraph(digraph).unwrap_err();
assert_eq!(err, NullGraphError);
assert_eq!(format!("{}", err), "graph does not contain any nodes or edges".to_string());
}

#[test]
fn module_index_fmt() {
let idx = ModuleIndex::new(42);
assert_eq!(format!("{:?}", idx), "ModuleIndex(42)".to_string())
}
}
Loading