mod functions;
use std::fmt::{Debug, Display, Formatter};
use std::iter::from_fn;
use petgraph::graph::DiGraph;
use petgraph::{Incoming, Outgoing};
use crate::index::make_index;
make_index!(pub(crate) NodeIndex);
#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum ModuleKind<NodeId: Copy + PartialEq> {
Prime,
Series,
Parallel,
Node(NodeId),
}
impl<NodeId: Debug + Copy + PartialEq> Debug for ModuleKind<NodeId> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ModuleKind::Prime => {
write!(f, "Prime")
}
ModuleKind::Series => {
write!(f, "Series")
}
ModuleKind::Parallel => {
write!(f, "Parallel")
}
ModuleKind::Node(v) => {
write!(f, "{v:?}")
}
}
}
}
#[derive(Clone, Debug)]
pub struct MDTree<NodeId: Copy + PartialEq> {
tree: DiGraph<ModuleKind<NodeId>, ()>,
root: ModuleIndex,
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct ModuleIndex(pub(crate) petgraph::graph::NodeIndex);
impl Debug for ModuleIndex {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("ModuleIndex").field(&self.0.index()).finish()
}
}
impl ModuleIndex {
pub fn new(x: usize) -> Self {
Self(petgraph::graph::NodeIndex::new(x))
}
pub fn index(&self) -> usize {
self.0.index()
}
}
impl<NodeId: Copy + PartialEq> MDTree<NodeId> {
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 })
}
#[inline(always)]
pub fn strong_module_count(&self) -> usize {
self.tree.node_count()
}
#[inline(always)]
pub fn root(&self) -> ModuleIndex {
self.root
}
pub fn module_kind(&self, module: ModuleIndex) -> Option<&ModuleKind<NodeId>> {
self.tree.node_weight(module.0)
}
pub fn module_kinds(&self) -> impl Iterator<Item = &ModuleKind<NodeId>> {
self.tree.node_weights()
}
pub fn children(&self, module: ModuleIndex) -> impl Iterator<Item = ModuleIndex> + '_ {
self.tree.neighbors_directed(module.0, Outgoing).map(ModuleIndex)
}
pub fn nodes(&self, module: ModuleIndex) -> impl Iterator<Item = NodeId> + '_ {
let mut stack = vec![module.0];
from_fn(move || {
while let Some(next) = stack.pop() {
if let Some(ModuleKind::Node(node)) = self.tree.node_weight(next) {
return Some(*node);
} else {
let children = self.tree.neighbors_directed(next, Outgoing);
stack.extend(children);
}
}
None
})
}
pub fn into_digraph(self) -> DiGraph<ModuleKind<NodeId>, ()> {
self.tree
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct NullGraphError;
impl Display for NullGraphError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str("graph does not contain any nodes or edges")
}
}
impl std::error::Error for NullGraphError {}
#[cfg(test)]
mod test {
use petgraph::graph::{DiGraph, NodeIndex};
use petgraph::visit::IntoNodeIdentifiers;
use petgraph::Outgoing;
use crate::md_tree::NullGraphError;
use crate::tests::{complete_graph, pace2023_exact_024};
use crate::{modular_decomposition, MDTree, ModuleIndex, ModuleKind};
#[test]
fn nodes() {
let graph = pace2023_exact_024();
let md = modular_decomposition(&graph).unwrap();
let mut module_nodes: Vec<(ModuleKind<_>, Vec<_>)> = (0..md.strong_module_count())
.map(ModuleIndex::new)
.map(|module| (*md.module_kind(module).unwrap(), md.nodes(module).map(|node| node.index()).collect()))
.collect();
module_nodes.iter_mut().for_each(|(_, nodes)| nodes.sort());
module_nodes.sort();
for (kind, nodes) in &module_nodes {
if nodes.len() == 1 {
assert_eq!(*kind, ModuleKind::Node(NodeIndex::new(nodes[0])));
}
if nodes.len() == graph.node_count() {
assert_eq!(*nodes, graph.node_identifiers().map(|node| node.index()).collect::<Vec<_>>());
}
}
module_nodes.retain(|(_, nodes)| nodes.len() > 1);
let expected = [
(
ModuleKind::Prime,
vec![
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 21, 22, 23, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39,
],
),
(ModuleKind::Series, vec![17, 18, 19]),
(ModuleKind::Series, vec![24, 25]),
(
ModuleKind::Parallel,
vec![
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
],
),
(ModuleKind::Parallel, vec![20, 30]),
];
assert_eq!(module_nodes, expected);
}
#[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())
}
}