use foldhash::fast::RandomState;
use hashbrown::{HashMap, HashSet};
use indexmap::IndexSet;
use std::hash::Hash;
use petgraph::algo::kosaraju_scc;
use petgraph::stable_graph::{NodeIndex, StableDiGraph};
use petgraph::visit::{
EdgeCount, EdgeRef, GraphBase, GraphProp, IntoEdgeReferences, IntoNeighbors,
IntoNeighborsDirected, IntoNodeReferences, NodeCount, NodeFiltered, NodeIndexable, NodeRef,
Visitable,
};
use petgraph::Directed;
use crate::graph_ext::EdgeFindable;
fn build_subgraph<G>(
graph: G,
nodes: &[NodeIndex],
) -> (StableDiGraph<(), ()>, HashMap<NodeIndex, NodeIndex>)
where
G: EdgeCount + NodeCount + IntoEdgeReferences + IntoNodeReferences + GraphBase + NodeIndexable,
<G as GraphBase>::NodeId: Hash + Eq,
{
let node_set: HashSet<NodeIndex> = nodes.iter().copied().collect();
let mut node_map: HashMap<NodeIndex, NodeIndex> = HashMap::with_capacity(nodes.len());
let node_filter =
|node: G::NodeId| -> bool { node_set.contains(&NodeIndex::new(graph.to_index(node))) };
let mut out_graph = StableDiGraph::<(), ()>::with_capacity(nodes.len(), graph.edge_count());
let filtered = NodeFiltered::from_fn(graph, node_filter);
for node in filtered.node_references() {
let new_node = out_graph.add_node(());
node_map.insert(NodeIndex::new(graph.to_index(node.id())), new_node);
}
for edge in filtered.edge_references() {
let new_source = *node_map
.get(&NodeIndex::new(graph.to_index(edge.source())))
.unwrap();
let new_target = *node_map
.get(&NodeIndex::new(graph.to_index(edge.target())))
.unwrap();
out_graph.add_edge(
NodeIndex::new(new_source.index()),
NodeIndex::new(new_target.index()),
(),
);
}
(out_graph, node_map)
}
fn unblock(
node: NodeIndex,
blocked: &mut HashSet<NodeIndex>,
block: &mut HashMap<NodeIndex, HashSet<NodeIndex>>,
) {
let mut stack: IndexSet<NodeIndex, RandomState> = IndexSet::with_hasher(RandomState::default());
stack.insert(node);
while let Some(stack_node) = stack.pop() {
if blocked.remove(&stack_node) {
match block.get_mut(&stack_node) {
Some(block_set) => {
block_set.drain().for_each(|n| {
stack.insert(n);
});
}
None => {
block.insert(stack_node, HashSet::new());
}
}
blocked.remove(&stack_node);
}
}
}
#[allow(clippy::too_many_arguments)]
fn process_stack(
start_node: NodeIndex,
stack: &mut Vec<(NodeIndex, IndexSet<NodeIndex, foldhash::fast::RandomState>)>,
path: &mut Vec<NodeIndex>,
closed: &mut HashSet<NodeIndex>,
blocked: &mut HashSet<NodeIndex>,
block: &mut HashMap<NodeIndex, HashSet<NodeIndex>>,
subgraph: &StableDiGraph<(), ()>,
reverse_node_map: &HashMap<NodeIndex, NodeIndex>,
) -> Option<Vec<NodeIndex>> {
while let Some((this_node, neighbors)) = stack.last_mut() {
if let Some(next_node) = neighbors.pop() {
if next_node == start_node {
let mut out_path: Vec<NodeIndex> = Vec::with_capacity(path.len());
for n in path {
out_path.push(reverse_node_map[n]);
closed.insert(*n);
}
return Some(out_path);
} else if blocked.insert(next_node) {
path.push(next_node);
stack.push((
next_node,
subgraph
.neighbors(next_node)
.collect::<IndexSet<NodeIndex, foldhash::fast::RandomState>>(),
));
closed.remove(&next_node);
blocked.insert(next_node);
continue;
}
}
if neighbors.is_empty() {
if closed.contains(this_node) {
unblock(*this_node, blocked, block);
} else {
for neighbor in subgraph.neighbors(*this_node) {
let block_neighbor = block.entry(neighbor).or_insert_with(HashSet::new);
block_neighbor.insert(*this_node);
}
}
stack.pop();
path.pop();
}
}
None
}
pub struct SimpleCycleIter {
scc: Vec<Vec<NodeIndex>>,
self_cycles: Option<Vec<NodeIndex>>,
path: Vec<NodeIndex>,
blocked: HashSet<NodeIndex>,
closed: HashSet<NodeIndex>,
block: HashMap<NodeIndex, HashSet<NodeIndex>>,
stack: Vec<(NodeIndex, IndexSet<NodeIndex, RandomState>)>,
start_node: NodeIndex,
node_map: HashMap<NodeIndex, NodeIndex>,
reverse_node_map: HashMap<NodeIndex, NodeIndex>,
subgraph: StableDiGraph<(), ()>,
}
impl SimpleCycleIter {
pub fn next<G>(&mut self, graph: G) -> Option<Vec<NodeIndex>>
where
G: IntoEdgeReferences
+ IntoNodeReferences
+ GraphBase
+ EdgeCount
+ NodeCount
+ NodeIndexable,
<G as GraphBase>::NodeId: Hash + Eq,
{
if self.self_cycles.is_some() {
let self_cycles = self.self_cycles.as_mut().unwrap();
let cycle_node = self_cycles.pop().unwrap();
if self_cycles.is_empty() {
self.self_cycles = None;
}
return Some(vec![cycle_node]);
}
let mut stack: Vec<(NodeIndex, IndexSet<NodeIndex, foldhash::fast::RandomState>)> =
std::mem::take(&mut self.stack);
let mut path: Vec<NodeIndex> = std::mem::take(&mut self.path);
let mut closed: HashSet<NodeIndex> = std::mem::take(&mut self.closed);
let mut blocked: HashSet<NodeIndex> = std::mem::take(&mut self.blocked);
let mut block: HashMap<NodeIndex, HashSet<NodeIndex>> = std::mem::take(&mut self.block);
let mut subgraph: StableDiGraph<(), ()> = std::mem::take(&mut self.subgraph);
let mut reverse_node_map: HashMap<NodeIndex, NodeIndex> =
std::mem::take(&mut self.reverse_node_map);
let mut node_map: HashMap<NodeIndex, NodeIndex> = std::mem::take(&mut self.node_map);
if let Some(res) = process_stack(
self.start_node,
&mut stack,
&mut path,
&mut closed,
&mut blocked,
&mut block,
&subgraph,
&reverse_node_map,
) {
self.stack = stack;
self.path = path;
self.closed = closed;
self.blocked = blocked;
self.block = block;
self.subgraph = subgraph;
self.reverse_node_map = reverse_node_map;
self.node_map = node_map;
return Some(res);
} else {
subgraph.remove_node(self.start_node);
self.scc
.extend(kosaraju_scc(&subgraph).into_iter().filter_map(|scc| {
if scc.len() > 1 {
let res = scc
.iter()
.map(|n| reverse_node_map[n])
.collect::<Vec<NodeIndex>>();
Some(res)
} else {
None
}
}));
}
while let Some(mut scc) = self.scc.pop() {
let temp = build_subgraph(graph, &scc);
subgraph = temp.0;
node_map = temp.1;
reverse_node_map = node_map.iter().map(|(k, v)| (*v, *k)).collect();
self.start_node = node_map[&scc.pop().unwrap()];
path = vec![self.start_node];
blocked = path.iter().copied().collect();
closed = HashSet::new();
block = HashMap::new();
stack = vec![(
self.start_node,
subgraph
.neighbors(self.start_node)
.collect::<IndexSet<NodeIndex, foldhash::fast::RandomState>>(),
)];
if let Some(res) = process_stack(
self.start_node,
&mut stack,
&mut path,
&mut closed,
&mut blocked,
&mut block,
&subgraph,
&reverse_node_map,
) {
self.stack = stack;
self.path = path;
self.closed = closed;
self.blocked = blocked;
self.block = block;
self.subgraph = subgraph;
self.reverse_node_map = reverse_node_map;
self.node_map = node_map;
return Some(res);
}
subgraph.remove_node(self.start_node);
self.scc
.extend(kosaraju_scc(&subgraph).into_iter().filter_map(|scc| {
if scc.len() > 1 {
let res = scc
.iter()
.map(|n| reverse_node_map[n])
.collect::<Vec<NodeIndex>>();
Some(res)
} else {
None
}
}));
}
None
}
}
pub fn johnson_simple_cycles<G>(
graph: G,
self_cycles: Option<Vec<<G as GraphBase>::NodeId>>,
) -> SimpleCycleIter
where
G: IntoEdgeReferences
+ IntoNodeReferences
+ GraphBase
+ EdgeCount
+ NodeCount
+ NodeIndexable
+ Clone
+ IntoNeighbors
+ IntoNeighborsDirected
+ Visitable
+ EdgeFindable
+ GraphProp<EdgeType = Directed>,
<G as GraphBase>::NodeId: Hash + Eq,
{
let self_cycles = self_cycles.map(|self_cycles_vec| {
self_cycles_vec
.into_iter()
.map(|n| NodeIndex::new(graph.to_index(n)))
.collect()
});
let strongly_connected_components: Vec<Vec<NodeIndex>> = kosaraju_scc(graph)
.into_iter()
.filter_map(|component| {
if component.len() > 1 {
Some(
component
.into_iter()
.map(|n| NodeIndex::new(graph.to_index(n)))
.collect(),
)
} else {
None
}
})
.collect();
SimpleCycleIter {
scc: strongly_connected_components,
self_cycles,
path: Vec::new(),
blocked: HashSet::new(),
closed: HashSet::new(),
block: HashMap::new(),
stack: Vec::new(),
start_node: NodeIndex::new(u32::MAX as usize),
node_map: HashMap::new(),
reverse_node_map: HashMap::new(),
subgraph: StableDiGraph::new(),
}
}
#[cfg(test)]
mod test_longest_path {
use super::*;
use petgraph::graph::DiGraph;
use petgraph::stable_graph::NodeIndex;
use petgraph::stable_graph::StableDiGraph;
#[test]
fn test_empty_graph() {
let graph: DiGraph<(), ()> = DiGraph::new();
let mut result: Vec<_> = Vec::new();
let mut cycle_iter = johnson_simple_cycles(&graph, None);
while let Some(cycle) = cycle_iter.next(&graph) {
result.push(cycle);
}
let expected: Vec<Vec<NodeIndex>> = Vec::new();
assert_eq!(expected, result)
}
#[test]
fn test_empty_stable_graph() {
let graph: StableDiGraph<(), ()> = StableDiGraph::new();
let mut result: Vec<_> = Vec::new();
let mut cycle_iter = johnson_simple_cycles(&graph, None);
while let Some(cycle) = cycle_iter.next(&graph) {
result.push(cycle);
}
let expected: Vec<Vec<NodeIndex>> = Vec::new();
assert_eq!(expected, result)
}
#[test]
fn test_figure_1() {
for k in 3..10 {
let mut graph: DiGraph<(), ()> = DiGraph::new();
let mut edge_list: Vec<[usize; 2]> = Vec::new();
for n in 2..k + 2 {
edge_list.push([1, n]);
edge_list.push([n, k + 2]);
}
edge_list.push([2 * k + 1, 1]);
for n in k + 2..2 * k + 2 {
edge_list.push([n, 2 * k + 2]);
edge_list.push([n, n + 1]);
}
edge_list.push([2 * k + 3, k + 2]);
for n in 2 * k + 3..3 * k + 3 {
edge_list.push([2 * k + 2, n]);
edge_list.push([n, 3 * k + 3]);
}
edge_list.push([3 * k + 3, 2 * k + 2]);
graph.extend_with_edges(
edge_list
.into_iter()
.map(|x| (NodeIndex::new(x[0]), NodeIndex::new(x[1]))),
);
let mut cycles_iter = johnson_simple_cycles(&graph, None);
let mut res = 0;
while cycles_iter.next(&graph).is_some() {
res += 1;
}
assert_eq!(res, 3 * k);
}
}
#[test]
fn test_figure_1_stable_graph() {
for k in 3..10 {
let mut graph: StableDiGraph<(), ()> = StableDiGraph::new();
let mut edge_list: Vec<[usize; 2]> = Vec::new();
for n in 2..k + 2 {
edge_list.push([1, n]);
edge_list.push([n, k + 2]);
}
edge_list.push([2 * k + 1, 1]);
for n in k + 2..2 * k + 2 {
edge_list.push([n, 2 * k + 2]);
edge_list.push([n, n + 1]);
}
edge_list.push([2 * k + 3, k + 2]);
for n in 2 * k + 3..3 * k + 3 {
edge_list.push([2 * k + 2, n]);
edge_list.push([n, 3 * k + 3]);
}
edge_list.push([3 * k + 3, 2 * k + 2]);
graph.extend_with_edges(
edge_list
.into_iter()
.map(|x| (NodeIndex::new(x[0]), NodeIndex::new(x[1]))),
);
let mut cycles_iter = johnson_simple_cycles(&graph, None);
let mut res = 0;
while cycles_iter.next(&graph).is_some() {
res += 1;
}
assert_eq!(res, 3 * k);
}
}
}