1use petgraph::graph::{DiGraph, Graph, NodeIndex};
4use petgraph::visit::{EdgeRef, IntoNodeIdentifiers};
5use petgraph::Directed;
6use serde::{Deserialize, Serialize};
7use std::collections::HashSet;
8
9pub fn graphs_equal<N, E, Ty>(g1: &Graph<N, E, Ty>, g2: &Graph<N, E, Ty>) -> bool
13where
14 N: PartialEq,
15 E: PartialEq,
16 Ty: petgraph::EdgeType,
17{
18 if g1.node_count() != g2.node_count() {
20 return false;
21 }
22
23 if g1.edge_count() != g2.edge_count() {
25 return false;
26 }
27
28 for node in g1.node_identifiers() {
30 let weight1 = g1.node_weight(node);
31 let weight2 = g2.node_weight(node);
32 if weight1 != weight2 {
33 return false;
34 }
35 }
36
37 for edge in g1.edge_references() {
39 let (source, target) = (edge.source(), edge.target());
40 let weight = edge.weight();
41
42 let mut found = false;
44 for edge2 in g2.edge_references() {
45 if edge2.source() == source && edge2.target() == target && edge2.weight() == weight {
46 found = true;
47 break;
48 }
49 }
50 if !found {
51 return false;
52 }
53 }
54
55 true
56}
57
58pub fn has_cycle<N, E>(g: &DiGraph<N, E>) -> bool
62where
63 N: Clone,
64{
65 use petgraph::algo::is_cyclic_directed;
66 is_cyclic_directed(g)
67}
68
69pub fn get_reachable_nodes<N, E>(g: &DiGraph<N, E>, source: NodeIndex) -> HashSet<NodeIndex>
73where
74 N: Clone,
75{
76 use petgraph::visit::Bfs;
77
78 let mut reachable = HashSet::new();
79 let mut bfs = Bfs::new(g, source);
80
81 while let Some(node) = bfs.next(g) {
82 reachable.insert(node);
83 }
84
85 reachable
86}
87
88pub fn is_strongly_connected<N, E>(g: &DiGraph<N, E>) -> bool
92where
93 N: Clone,
94{
95 if g.node_count() == 0 {
96 return false;
97 }
98
99 use petgraph::visit::{Dfs, VisitMap, Visitable};
100 use petgraph::Direction;
101
102 let start = g.node_indices().next().unwrap();
104 let mut dfs = Dfs::new(g, start);
105 let mut reachable = 0;
106 while dfs.next(g).is_some() {
107 reachable += 1;
108 }
109
110 if reachable != g.node_count() {
111 return false;
112 }
113
114 let _dfs = Dfs::new(&g, start);
116 let mut reverse_reachable = 0;
117
118 let mut visited = g.visit_map();
120 let mut stack = vec![start];
121 while let Some(node) = stack.pop() {
122 if visited.is_visited(&node) {
123 continue;
124 }
125 visited.visit(node);
126 reverse_reachable += 1;
127
128 for neighbor in g.neighbors_directed(node, Direction::Incoming) {
129 if !visited.is_visited(&neighbor) {
130 stack.push(neighbor);
131 }
132 }
133 }
134
135 reverse_reachable == g.node_count()
136}
137
138pub fn count_connected_components<N, E>(g: &Graph<N, E>) -> usize
140where
141 N: Clone,
142{
143 use petgraph::algo::connected_components;
144 connected_components(g)
145}
146
147pub fn get_node_degrees<N, E, Ty>(g: &Graph<N, E, Ty>) -> Vec<usize>
149where
150 Ty: petgraph::EdgeType,
151{
152 g.node_indices().map(|node| g.edges(node).count()).collect()
153}
154
155pub fn get_in_out_degrees<N, E>(g: &DiGraph<N, E>) -> Vec<(usize, usize)> {
159 g.node_indices()
160 .map(|node| {
161 let in_degree = g
162 .edges_directed(node, petgraph::Direction::Incoming)
163 .count();
164 let out_degree = g
165 .edges_directed(node, petgraph::Direction::Outgoing)
166 .count();
167 (in_degree, out_degree)
168 })
169 .collect()
170}
171
172pub fn serialize_graph<N, E, Ty>(g: &Graph<N, E, Ty>) -> Result<String, serde_json::Error>
176where
177 N: Serialize + Clone,
178 E: Serialize + Clone,
179 Ty: petgraph::EdgeType + 'static,
180{
181 #[derive(Serialize)]
182 #[allow(dead_code)]
183 struct GraphJSON<N, E> {
184 nodes: Vec<(usize, N)>,
185 edges: Vec<(usize, usize, E)>,
186 directed: bool,
187 }
188
189 let nodes: Vec<(usize, N)> = g
190 .node_indices()
191 .map(|node| (node.index(), g[node].clone()))
192 .collect();
193
194 let edges: Vec<(usize, usize, E)> = g
195 .edge_indices()
196 .map(|edge| {
197 let (source, target) = g.edge_endpoints(edge).unwrap();
198 (source.index(), target.index(), g[edge].clone())
199 })
200 .collect();
201
202 let graph_json = GraphJSON {
203 nodes,
204 edges,
205 directed: false,
206 };
207
208 serde_json::to_string_pretty(&graph_json)
209}
210
211pub fn deserialize_graph<N, E>(json: &str) -> Result<Graph<N, E>, serde_json::Error>
215where
216 N: for<'de> Deserialize<'de>,
217 E: for<'de> Deserialize<'de>,
218{
219 #[derive(Deserialize)]
220 #[allow(dead_code)]
221 struct GraphJSON<N, E> {
222 nodes: Vec<(usize, N)>,
223 edges: Vec<(usize, usize, E)>,
224 directed: bool,
225 }
226
227 let graph_json: GraphJSON<N, E> = serde_json::from_str(json)?;
228
229 let mut graph = Graph::new();
230 let mut node_indices = Vec::new();
231
232 for (_original_index, weight) in graph_json.nodes {
234 let idx = graph.add_node(weight);
235 node_indices.push(idx);
236 }
237
238 for (source_idx, target_idx, weight) in graph_json.edges {
240 let source = node_indices[source_idx];
241 let target = node_indices[target_idx];
242 graph.add_edge(source, target, weight);
243 }
244
245 Ok(graph)
246}
247
248pub fn to_dot<N, E, Ty>(g: &Graph<N, E, Ty>) -> String
250where
251 N: std::fmt::Display,
252 E: std::fmt::Display,
253 Ty: petgraph::EdgeType + 'static,
254{
255 let is_directed = std::any::TypeId::of::<Ty>() == std::any::TypeId::of::<Directed>();
256 let edge_connector = if is_directed { "->" } else { "--" };
257
258 let mut dot = String::new();
259
260 if is_directed {
261 dot.push_str("digraph G {\n");
262 } else {
263 dot.push_str("graph G {\n");
264 }
265
266 for node in g.node_indices() {
268 dot.push_str(&format!(" {} [label=\"{}\"];", node.index(), g[node]));
269 }
270
271 for edge in g.edge_references() {
273 let (source, target) = (edge.source(), edge.target());
274 dot.push_str(&format!(
275 " {} {} {} [label=\"{}\"];",
276 source.index(),
277 edge_connector,
278 target.index(),
279 edge.weight()
280 ));
281 }
282
283 dot.push('}');
284 dot
285}
286
287#[cfg(test)]
288mod tests {
289 use super::*;
290
291 #[test]
292 fn test_graphs_equal() {
293 let g1 = Graph::<(), i32>::from_edges([(0, 1, 5), (1, 2, 3)]);
294 let g2 = Graph::<(), i32>::from_edges([(0, 1, 5), (1, 2, 3)]);
295 let g3 = Graph::<(), i32>::from_edges([(0, 1, 5), (1, 2, 4)]);
296
297 assert!(graphs_equal(&g1, &g2));
298 assert!(!graphs_equal(&g1, &g3));
299 }
300
301 #[test]
302 fn test_has_cycle() {
303 let cyclic = DiGraph::<(), i32>::from_edges([(0, 1, 1), (1, 2, 1), (2, 0, 1)]);
304 let acyclic = DiGraph::<(), i32>::from_edges([(0, 1, 1), (1, 2, 1)]);
305
306 assert!(has_cycle(&cyclic));
307 assert!(!has_cycle(&acyclic));
308 }
309
310 #[test]
311 fn test_get_reachable_nodes() {
312 let graph = DiGraph::<(), i32>::from_edges([(0, 1, 1), (1, 2, 1), (0, 3, 1), (4, 5, 1)]);
313
314 let reachable = get_reachable_nodes(&graph, NodeIndex::new(0));
315 assert_eq!(reachable.len(), 4);
316 assert!(reachable.contains(&NodeIndex::new(0)));
317 assert!(reachable.contains(&NodeIndex::new(1)));
318 assert!(reachable.contains(&NodeIndex::new(2)));
319 assert!(reachable.contains(&NodeIndex::new(3)));
320 }
321
322 #[test]
323 fn test_is_strongly_connected() {
324 let strongly_connected = DiGraph::<(), i32>::from_edges([(0, 1, 1), (1, 2, 1), (2, 0, 1)]);
325
326 let not_strongly_connected = DiGraph::<(), i32>::from_edges([(0, 1, 1), (1, 2, 1)]);
327
328 assert!(is_strongly_connected(&strongly_connected));
329 assert!(!is_strongly_connected(¬_strongly_connected));
330 }
331
332 #[test]
333 fn test_count_connected_components() {
334 let graph = Graph::<(), i32>::from_edges([(0, 1, 1), (1, 2, 1), (3, 4, 1)]);
335
336 assert_eq!(count_connected_components(&graph), 2);
337 }
338
339 #[test]
340 fn test_get_node_degrees() {
341 use petgraph::Undirected;
342 let graph = Graph::<(), i32, Undirected>::from_edges([(0, 1, 1), (1, 2, 1)]);
343 let degrees = get_node_degrees(&graph);
344
345 assert_eq!(degrees[0], 1);
346 assert_eq!(degrees[1], 2);
347 assert_eq!(degrees[2], 1);
348 }
349
350 #[test]
351 fn test_get_in_out_degrees() {
352 let graph = DiGraph::<(), i32>::from_edges([(0, 1, 1), (1, 2, 1), (2, 1, 1)]);
353
354 let degrees = get_in_out_degrees(&graph);
355 assert_eq!(degrees[0], (0, 1));
356 assert_eq!(degrees[1], (2, 1));
357 assert_eq!(degrees[2], (1, 1));
358 }
359
360 #[test]
361 fn test_serialize_deserialize_graph() {
362 let graph = Graph::<i32, f64>::from_edges([(0, 1, 1.5), (1, 2, 2.5)]);
363
364 let json = serialize_graph(&graph).unwrap();
365 let deserialized = deserialize_graph::<i32, f64>(&json).unwrap();
366
367 assert!(graphs_equal(&graph, &deserialized));
368 }
369
370 #[test]
371 fn test_to_dot() {
372 let graph = DiGraph::<i32, f64>::from_edges([(0, 1, 1.5), (1, 2, 2.5)]);
373 let dot = to_dot(&graph);
374
375 assert!(dot.contains("digraph G"));
376 assert!(dot.contains("->"));
377 }
378}