rustworkx_core/connectivity/
cycle_basis.rs1use hashbrown::{HashMap, HashSet};
14use petgraph::visit::{IntoNeighbors, IntoNodeIdentifiers, NodeCount};
15use std::hash::Hash;
16
17pub fn cycle_basis<G>(graph: G, root: Option<G::NodeId>) -> Vec<Vec<G::NodeId>>
49where
50 G: NodeCount,
51 G: IntoNeighbors,
52 G: IntoNodeIdentifiers,
53 G::NodeId: Eq + Hash,
54{
55 let mut root_node = root;
56 let mut graph_nodes: HashSet<G::NodeId> = graph.node_identifiers().collect();
57 let mut cycles: Vec<Vec<G::NodeId>> = Vec::new();
58 while !graph_nodes.is_empty() {
59 let temp_value: G::NodeId;
60 let root_index = match root_node {
63 Some(root_node) => root_node,
64 None => {
65 temp_value = *graph_nodes.iter().next().unwrap();
66 graph_nodes.remove(&temp_value);
67 temp_value
68 }
69 };
70 let mut stack: Vec<G::NodeId> = vec![root_index];
72 let mut pred: HashMap<G::NodeId, G::NodeId> = HashMap::new();
74 pred.insert(root_index, root_index);
75 let mut used: HashMap<G::NodeId, HashSet<G::NodeId>> = HashMap::new();
77 used.insert(root_index, HashSet::new());
78 while let Some(z) = stack.pop() {
81 for neighbor in graph.neighbors(z) {
82 if !used.contains_key(&neighbor) {
84 pred.insert(neighbor, z);
85 stack.push(neighbor);
86 let mut temp_set: HashSet<G::NodeId> = HashSet::new();
87 temp_set.insert(z);
88 used.insert(neighbor, temp_set);
89 } else if z == neighbor {
91 let cycle: Vec<G::NodeId> = vec![z];
92 cycles.push(cycle);
93 } else if !used.get(&z).unwrap().contains(&neighbor) {
95 let prev_n = used.get(&neighbor).unwrap();
96 let mut cycle: Vec<G::NodeId> = vec![neighbor, z];
97 let mut p = pred.get(&z).unwrap();
98 while !prev_n.contains(p) {
99 cycle.push(*p);
100 p = pred.get(p).unwrap();
101 }
102 cycle.push(*p);
103 cycles.push(cycle);
104 let neighbor_set = used.get_mut(&neighbor).unwrap();
105 neighbor_set.insert(z);
106 }
107 }
108 }
109 let mut temp_hashset: HashSet<G::NodeId> = HashSet::new();
110 for key in pred.keys() {
111 temp_hashset.insert(*key);
112 }
113 graph_nodes = graph_nodes.difference(&temp_hashset).copied().collect();
114 root_node = None;
115 }
116 cycles
117}
118
119#[cfg(test)]
120mod tests {
121 use crate::connectivity::cycle_basis;
122 use petgraph::prelude::*;
123
124 fn sorted_cycle(cycles: Vec<Vec<NodeIndex>>) -> Vec<Vec<usize>> {
125 let mut sorted_cycles: Vec<Vec<usize>> = vec![];
126 for cycle in cycles {
127 let mut cycle: Vec<usize> = cycle.iter().map(|x| x.index()).collect();
128 cycle.sort();
129 sorted_cycles.push(cycle);
130 }
131 sorted_cycles.sort();
132 sorted_cycles
133 }
134
135 #[test]
136 fn test_cycle_basis_source() {
137 let edge_list = vec![
138 (0, 1),
139 (0, 3),
140 (0, 5),
141 (0, 8),
142 (1, 2),
143 (1, 6),
144 (2, 3),
145 (3, 4),
146 (4, 5),
147 (6, 7),
148 (7, 8),
149 (8, 9),
150 ];
151 let graph = UnGraph::<i32, i32>::from_edges(edge_list);
152 let expected = vec![vec![0, 1, 2, 3], vec![0, 1, 6, 7, 8], vec![0, 3, 4, 5]];
153 let res_0 = cycle_basis(&graph, Some(NodeIndex::new(0)));
154 assert_eq!(sorted_cycle(res_0), expected);
155 let res_1 = cycle_basis(&graph, Some(NodeIndex::new(1)));
156 assert_eq!(sorted_cycle(res_1), expected);
157 let res_9 = cycle_basis(&graph, Some(NodeIndex::new(9)));
158 assert_eq!(sorted_cycle(res_9), expected);
159 }
160
161 #[test]
162 fn test_self_loop() {
163 let edge_list = vec![
164 (0, 1),
165 (0, 3),
166 (0, 5),
167 (0, 8),
168 (1, 2),
169 (1, 6),
170 (2, 3),
171 (3, 4),
172 (4, 5),
173 (6, 7),
174 (7, 8),
175 (8, 9),
176 ];
177 let mut graph = UnGraph::<i32, i32>::from_edges(edge_list);
178 graph.add_edge(NodeIndex::new(1), NodeIndex::new(1), 0);
179 let res_0 = cycle_basis(&graph, Some(NodeIndex::new(0)));
180 assert_eq!(
181 sorted_cycle(res_0),
182 vec![
183 vec![0, 1, 2, 3],
184 vec![0, 1, 6, 7, 8],
185 vec![0, 3, 4, 5],
186 vec![1]
187 ]
188 );
189 }
190}