1#[cfg(any(test, feature = "test-utils"))]
2pub mod utils;
3
4use ade_common::INVALID_KEY_SEQUENCE;
5use ade_strongly_connected_components::scc_iterative;
6use ade_traits::{EdgeTrait, GraphViewTrait, NodeTrait};
7use smallvec::SmallVec;
8
9pub fn elementary_circuits<N: NodeTrait, E: EdgeTrait>(
13 graph: &impl GraphViewTrait<N, E>,
14) -> Vec<Vec<u32>> {
15 if !graph.has_sequential_keys() {
17 panic!("{}", INVALID_KEY_SEQUENCE);
18 }
19
20 let mut circuits: Vec<Vec<u32>> = Vec::new();
22 let mut stack: Vec<u32> = Vec::new();
23
24 let n = match graph.get_nodes().count() {
25 0 => return circuits, len => (len - 1) as u32,
27 };
28
29 let mut s: u32 = n; let size = (n + 1) as usize;
31 let mut blocked_set: Vec<bool> = vec![false; size];
32 let mut blocked_map: Vec<SmallVec<[u32; 4]>> = vec![SmallVec::new(); size];
33
34 loop {
35 let remaining_nodes: Vec<u32> = (0..=s).collect();
37 let subgraph = graph.filter(&remaining_nodes);
38
39 let components = scc_iterative(&subgraph);
41
42 let component = components.iter().find(|c| c.contains(&s)).unwrap();
44
45 let adj = subgraph.filter(component);
47
48 if adj.get_nodes().next().is_some() {
50 for key in adj.get_node_keys() {
51 let k = key as usize;
52 blocked_set[k] = false;
53 blocked_map[k].clear();
54 }
55
56 find_circuit(
57 s,
58 s,
59 &mut circuits,
60 &mut stack,
61 &mut blocked_set,
62 &mut blocked_map,
63 &adj,
64 );
65 if s == 0 {
66 break;
67 }
68 s -= 1;
69 } else {
70 s = 0;
71 }
72 }
73
74 circuits
75}
76
77fn find_circuit<N: NodeTrait, E: EdgeTrait>(
78 s: u32,
79 v: u32,
80 circuits: &mut Vec<Vec<u32>>,
81 stack: &mut Vec<u32>,
82 blocked_set: &mut [bool],
83 blocked_map: &mut [SmallVec<[u32; 4]>],
84 adj: &impl GraphViewTrait<N, E>,
85) -> bool {
86 let mut f: bool = false;
87 let v_us = v as usize;
88
89 stack.push(v);
90 blocked_set[v_us] = true;
91
92 for w_key in adj.get_successors_keys(v) {
93 if w_key == s {
94 let mut circuit = Vec::with_capacity(stack.len() + 1);
95 circuit.extend_from_slice(stack);
96 circuit.push(s);
97 circuits.push(circuit);
98 f = true;
99 } else if !blocked_set[w_key as usize]
100 && find_circuit(s, w_key, circuits, stack, blocked_set, blocked_map, adj)
101 {
102 f = true;
103 }
104 }
105
106 if f {
107 unblock(v, blocked_set, blocked_map);
108 } else {
109 for w_key in adj.get_successors_keys(v) {
110 let list = &mut blocked_map[w_key as usize];
111 if !list.contains(&v) {
112 list.push(v);
113 }
114 }
115 }
116
117 stack.pop();
118 f
119}
120
121fn unblock(u: u32, blocked_set: &mut [bool], blocked_map: &mut [SmallVec<[u32; 4]>]) {
122 let u_us = u as usize;
123 blocked_set[u_us] = false;
124
125 while let Some(w) = blocked_map[u_us].pop() {
126 let w_us = w as usize;
127 if blocked_set[w_us] {
128 unblock(w, blocked_set, blocked_map);
129 }
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136 use crate::utils::circuits_equal;
137 use crate::utils::number_circuits;
138 use ade_common::{self, assert_panics_with};
139 use ade_graph::utils::build::build_graph;
140 use ade_graph_generators::complete_graph_data;
141 use ade_graph_generators::generate_random_graph_data;
142 use graph_cycles::Cycles;
143 use petgraph::graph::Graph as PetGraph;
144 use rand::Rng;
145 use std::collections::HashSet;
146
147 #[test]
148 fn test_elementary_circuits_1() {
149 let graph = build_graph(vec![0, 1, 2], vec![(0, 1), (1, 2), (2, 1)]);
150 let circuits = elementary_circuits(&graph);
151 let expected = vec![vec![1, 2, 1]];
152
153 assert!(circuits_equal(&circuits, &expected));
154 }
155
156 #[test]
157 fn test_elementary_circuits_non_sequential_keys() {
158 let graph = build_graph(vec![1, 3, 5], vec![(1, 3), (3, 5), (5, 1)]);
159 assert_panics_with!(
160 elementary_circuits(&graph),
161 ade_common::INVALID_KEY_SEQUENCE
162 );
163 }
164
165 #[test]
166 fn test_elementary_circuits_3() {
167 let graph = build_graph(vec![0, 1, 2], vec![(0, 1), (1, 2)]);
168 let circuits = elementary_circuits(&graph);
169 let expected = vec![];
170
171 assert!(circuits_equal(&circuits, &expected));
172 }
173
174 #[test]
175 fn test_elementary_circuits_4() {
176 let graph = build_graph(vec![0, 1, 2], vec![]);
177 let circuits = elementary_circuits(&graph);
178 let expected = vec![];
179
180 assert!(circuits_equal(&circuits, &expected));
181 }
182
183 #[test]
184 fn test_elementary_circuits_5() {
185 let graph = build_graph(vec![], vec![]);
186 let circuits = elementary_circuits(&graph);
187 let expected = vec![];
188
189 assert!(circuits_equal(&circuits, &expected));
190 }
191
192 #[test]
193 fn test_elementary_circuits_6() {
194 let graph = build_graph(vec![0], vec![(0, 0)]);
195 let circuits = elementary_circuits(&graph);
196 let expected = vec![vec![0, 0]];
197
198 assert!(circuits_equal(&circuits, &expected));
199 }
200
201 #[test]
202 fn test_elementary_circuits_7() {
203 let graph = build_graph(vec![0, 1], vec![(0, 0), (1, 1), (0, 1)]);
204 let circuits = elementary_circuits(&graph);
205 let expected = vec![vec![0, 0], vec![1, 1]];
206
207 assert!(circuits_equal(&circuits, &expected));
208 }
209
210 #[test]
211 fn test_elementary_circuits_2() {
212 let graph = build_graph(
213 vec![0, 1, 2, 3, 4, 5, 6, 7, 8],
214 vec![
215 (0, 1),
216 (0, 7),
217 (0, 4),
218 (1, 2),
219 (1, 6),
220 (1, 8),
221 (2, 1),
222 (2, 0),
223 (2, 3),
224 (2, 5),
225 (3, 4),
226 (4, 1),
227 (5, 3),
228 (7, 8),
229 (8, 7),
230 ],
231 );
232 let circuits = elementary_circuits(&graph);
233 let expected: Vec<Vec<u32>> = vec![
234 vec![0, 1, 2, 0],
235 vec![0, 4, 1, 2, 0],
236 vec![1, 2, 1],
237 vec![1, 2, 3, 4, 1],
238 vec![1, 2, 5, 3, 4, 1],
239 vec![7, 8, 7],
240 ];
241
242 assert!(circuits_equal(&circuits, &expected));
243 }
244
245 #[test]
246 fn test_elementary_circuits_complete_graph() {
247 let n: usize = 6;
248 let (nodes, edges) = complete_graph_data(n);
249 let graph = build_graph(nodes, edges);
250 let circuits = elementary_circuits(&graph);
251
252 assert_eq!(circuits.len(), number_circuits(n))
253 }
254
255 #[test]
282 fn test_elementary_circuits_multiple_random_graphs() {
283 let mut rng = rand::thread_rng();
284
285 for _ in 0..100 {
286 let node_count = rng.gen_range(5..20);
287 let edge_count = rng.gen_range(node_count * 2..node_count * 5);
288 let seed = rng.gen();
289
290 let (nodes, edges) = generate_random_graph_data(node_count, edge_count, seed);
291
292 let mut seen = HashSet::new();
294 let mut unique_edges = Vec::new();
295 for edge in edges {
296 if seen.insert(edge) {
297 unique_edges.push(edge);
298 }
299 }
300
301 let graph = build_graph(nodes.clone(), unique_edges.clone());
302 let circuits = elementary_circuits(&graph);
303
304 let g = PetGraph::<(), ()>::from_edges(unique_edges.clone());
305 let cycles = g.cycles();
306
307 assert_eq!(
308 circuits.len(),
309 cycles.len(),
310 "Mismatch con seed={:?}, nodi={}, edges={}",
311 seed,
312 node_count,
313 edge_count
314 );
315 }
316 }
317}