rust_igraph/algorithms/operators/
connect_neighborhood.rs1use std::collections::VecDeque;
6
7use crate::core::{Graph, IgraphResult, VertexId};
8
9pub fn connect_neighborhood(graph: &Graph, order: u32) -> IgraphResult<Graph> {
42 let n = graph.vcount();
43 let directed = graph.is_directed();
44
45 if order < 2 || n == 0 {
46 let mut result = Graph::new(n, directed)?;
48 let ecount = graph.ecount();
49 let mut edges: Vec<(VertexId, VertexId)> = Vec::with_capacity(ecount);
50 for eid in 0..ecount {
51 #[allow(clippy::cast_possible_truncation)]
52 let eid_u32 = eid as u32;
53 edges.push(graph.edge(eid_u32)?);
54 }
55 result.add_edges(edges)?;
56 return Ok(result);
57 }
58
59 let adj = build_adjacency_list(graph, directed)?;
61
62 let mut new_edges: Vec<(VertexId, VertexId)> = Vec::new();
64 let mut visited = vec![0u32; n as usize];
65
66 for i in 0..n {
67 let marker = i + 1;
68 visited[i as usize] = marker;
69
70 for &nei in &adj[i as usize] {
72 visited[nei as usize] = marker;
73 }
74
75 let mut queue: VecDeque<(VertexId, u32)> = VecDeque::new();
77 for &nei in &adj[i as usize] {
78 queue.push_back((nei, 1));
79 }
80
81 while let Some((node, dist)) = queue.pop_front() {
82 if dist >= order {
83 continue;
84 }
85 for &nei in &adj[node as usize] {
86 if visited[nei as usize] != marker {
87 visited[nei as usize] = marker;
88 if directed || i < nei {
89 new_edges.push((i, nei));
90 }
91 if dist + 1 < order {
92 queue.push_back((nei, dist + 1));
93 }
94 }
95 }
96 }
97 }
98
99 let ecount = graph.ecount();
101 let mut all_edges: Vec<(VertexId, VertexId)> = Vec::with_capacity(ecount + new_edges.len());
102 for eid in 0..ecount {
103 #[allow(clippy::cast_possible_truncation)]
104 let eid_u32 = eid as u32;
105 all_edges.push(graph.edge(eid_u32)?);
106 }
107 all_edges.extend(new_edges);
108
109 let mut result = Graph::new(n, directed)?;
110 result.add_edges(all_edges)?;
111 Ok(result)
112}
113
114pub fn graph_power(graph: &Graph, order: u32) -> IgraphResult<Graph> {
145 let n = graph.vcount();
146 let directed = graph.is_directed();
147
148 if order == 0 {
149 return Graph::new(n, directed);
150 }
151
152 let adj = build_simple_adjacency_list(graph, directed)?;
154
155 if order == 1 {
156 return build_graph_from_adj(&adj, n, directed);
158 }
159
160 let mut edges: Vec<(VertexId, VertexId)> = Vec::new();
162 let mut visited = vec![0u32; n as usize];
163
164 for i in 0..n {
165 let marker = i + 1;
166 visited[i as usize] = marker;
167
168 let mut queue: VecDeque<(VertexId, u32)> = VecDeque::new();
169
170 for &nei in &adj[i as usize] {
172 if visited[nei as usize] != marker {
173 visited[nei as usize] = marker;
174 if directed || i < nei {
175 edges.push((i, nei));
176 }
177 if order > 1 {
178 queue.push_back((nei, 1));
179 }
180 }
181 }
182
183 while let Some((node, dist)) = queue.pop_front() {
185 if dist >= order {
186 continue;
187 }
188 for &nei in &adj[node as usize] {
189 if visited[nei as usize] != marker {
190 visited[nei as usize] = marker;
191 if directed || i < nei {
192 edges.push((i, nei));
193 }
194 if dist + 1 < order {
195 queue.push_back((nei, dist + 1));
196 }
197 }
198 }
199 }
200 }
201
202 let mut result = Graph::new(n, directed)?;
203 result.add_edges(edges)?;
204 Ok(result)
205}
206
207fn build_adjacency_list(graph: &Graph, _directed: bool) -> IgraphResult<Vec<Vec<VertexId>>> {
208 let n = graph.vcount() as usize;
209 let mut adj: Vec<Vec<VertexId>> = vec![Vec::new(); n];
210 let ecount = graph.ecount();
211
212 for eid in 0..ecount {
213 #[allow(clippy::cast_possible_truncation)]
214 let eid_u32 = eid as u32;
215 let (src, tgt) = graph.edge(eid_u32)?;
216 adj[src as usize].push(tgt);
217 if !graph.is_directed() && src != tgt {
218 adj[tgt as usize].push(src);
219 }
220 }
221 Ok(adj)
222}
223
224fn build_simple_adjacency_list(graph: &Graph, _directed: bool) -> IgraphResult<Vec<Vec<VertexId>>> {
225 let n = graph.vcount() as usize;
226 let mut adj: Vec<Vec<VertexId>> = vec![Vec::new(); n];
227 let ecount = graph.ecount();
228
229 for eid in 0..ecount {
230 #[allow(clippy::cast_possible_truncation)]
231 let eid_u32 = eid as u32;
232 let (src, tgt) = graph.edge(eid_u32)?;
233 if src == tgt {
234 continue; }
236 adj[src as usize].push(tgt);
237 if !graph.is_directed() {
238 adj[tgt as usize].push(src);
239 }
240 }
241
242 for list in &mut adj {
244 list.sort_unstable();
245 list.dedup();
246 }
247 Ok(adj)
248}
249
250fn build_graph_from_adj(adj: &[Vec<VertexId>], n: u32, directed: bool) -> IgraphResult<Graph> {
251 let mut edges: Vec<(VertexId, VertexId)> = Vec::new();
252 for (i, neighbors) in adj.iter().enumerate() {
253 #[allow(clippy::cast_possible_truncation)]
254 let i_u32 = i as u32;
255 for &nei in neighbors {
256 if directed || i_u32 < nei {
257 edges.push((i_u32, nei));
258 }
259 }
260 }
261 let mut result = Graph::new(n, directed)?;
262 result.add_edges(edges)?;
263 Ok(result)
264}
265
266#[cfg(test)]
267mod tests {
268 use super::*;
269
270 #[test]
271 fn test_connect_neighborhood_path() {
272 let mut g = Graph::with_vertices(4);
274 g.add_edge(0, 1).unwrap();
275 g.add_edge(1, 2).unwrap();
276 g.add_edge(2, 3).unwrap();
277
278 let cg = connect_neighborhood(&g, 2).unwrap();
279 assert_eq!(cg.vcount(), 4);
280 assert_eq!(cg.ecount(), 5);
282 }
283
284 #[test]
285 fn test_connect_neighborhood_order_1() {
286 let mut g = Graph::with_vertices(3);
287 g.add_edge(0, 1).unwrap();
288
289 let cg = connect_neighborhood(&g, 1).unwrap();
290 assert_eq!(cg.ecount(), 1); }
292
293 #[test]
294 fn test_connect_neighborhood_order_0() {
295 let mut g = Graph::with_vertices(3);
296 g.add_edge(0, 1).unwrap();
297
298 let cg = connect_neighborhood(&g, 0).unwrap();
299 assert_eq!(cg.ecount(), 1); }
301
302 #[test]
303 fn test_connect_neighborhood_complete() {
304 let mut g = Graph::with_vertices(3);
306 g.add_edge(0, 1).unwrap();
307 g.add_edge(0, 2).unwrap();
308 g.add_edge(1, 2).unwrap();
309
310 let cg = connect_neighborhood(&g, 2).unwrap();
311 assert_eq!(cg.ecount(), 3);
312 }
313
314 #[test]
315 fn test_connect_neighborhood_large_order() {
316 let mut g = Graph::with_vertices(4);
318 g.add_edge(0, 1).unwrap();
319 g.add_edge(1, 2).unwrap();
320 g.add_edge(2, 3).unwrap();
321
322 let cg = connect_neighborhood(&g, 10).unwrap();
323 assert_eq!(cg.ecount(), 6); }
325
326 #[test]
327 fn test_connect_neighborhood_empty() {
328 let g = Graph::with_vertices(0);
329 let cg = connect_neighborhood(&g, 5).unwrap();
330 assert_eq!(cg.vcount(), 0);
331 }
332
333 #[test]
334 fn test_connect_neighborhood_isolated() {
335 let g = Graph::with_vertices(5);
336 let cg = connect_neighborhood(&g, 3).unwrap();
337 assert_eq!(cg.ecount(), 0);
338 }
339
340 #[test]
341 fn test_graph_power_zero() {
342 let mut g = Graph::with_vertices(3);
343 g.add_edge(0, 1).unwrap();
344
345 let p = graph_power(&g, 0).unwrap();
346 assert_eq!(p.vcount(), 3);
347 assert_eq!(p.ecount(), 0);
348 }
349
350 #[test]
351 fn test_graph_power_one() {
352 let mut g = Graph::with_vertices(3);
353 g.add_edge(0, 1).unwrap();
354 g.add_edge(1, 2).unwrap();
355
356 let p = graph_power(&g, 1).unwrap();
357 assert_eq!(p.vcount(), 3);
358 assert_eq!(p.ecount(), 2);
359 }
360
361 #[test]
362 fn test_graph_power_two_path() {
363 let mut g = Graph::with_vertices(4);
365 g.add_edge(0, 1).unwrap();
366 g.add_edge(1, 2).unwrap();
367 g.add_edge(2, 3).unwrap();
368
369 let p = graph_power(&g, 2).unwrap();
370 assert_eq!(p.vcount(), 4);
371 assert_eq!(p.ecount(), 5);
373 }
374
375 #[test]
376 fn test_graph_power_removes_self_loops() {
377 let mut g = Graph::with_vertices(3);
378 g.add_edge(0, 0).unwrap(); g.add_edge(0, 1).unwrap();
380 g.add_edge(1, 2).unwrap();
381
382 let p = graph_power(&g, 1).unwrap();
383 assert_eq!(p.ecount(), 2);
385 }
386
387 #[test]
388 fn test_graph_power_removes_multi_edges() {
389 let mut g = Graph::with_vertices(3);
390 g.add_edge(0, 1).unwrap();
391 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap();
393
394 let p = graph_power(&g, 1).unwrap();
395 assert_eq!(p.ecount(), 2);
396 }
397
398 #[test]
399 fn test_graph_power_directed() {
400 let mut g = Graph::new(3, true).unwrap();
402 g.add_edge(0, 1).unwrap();
403 g.add_edge(1, 2).unwrap();
404
405 let p = graph_power(&g, 2).unwrap();
406 assert!(p.is_directed());
407 assert_eq!(p.vcount(), 3);
408 assert_eq!(p.ecount(), 3);
410 }
411}