rust_igraph/algorithms/operators/
subgraph_from_edges.rs1use crate::core::{Graph, IgraphError, IgraphResult, VertexId};
10
11#[derive(Debug, Clone)]
13pub struct SubgraphFromEdgesResult {
14 pub graph: Graph,
16 pub vertex_map: Vec<u32>,
20 pub vertex_invmap: Vec<VertexId>,
22 pub edge_map: Vec<u32>,
24}
25
26pub fn subgraph_from_edges(
63 graph: &Graph,
64 eids: &[u32],
65 delete_vertices: bool,
66) -> IgraphResult<SubgraphFromEdgesResult> {
67 let n = graph.vcount();
68 let m = graph.ecount();
69
70 let mut edge_set: Vec<bool> = vec![false; m];
72 for &eid in eids {
73 if eid as usize >= m {
74 return Err(IgraphError::InvalidArgument(format!(
75 "subgraph_from_edges: edge ID {eid} out of range (graph has {m} edges)"
76 )));
77 }
78 edge_set[eid as usize] = true;
79 }
80
81 let mut vertex_remain: Vec<bool> = if delete_vertices {
83 vec![false; n as usize]
84 } else {
85 vec![true; n as usize]
86 };
87
88 let mut retained_edges: Vec<(u32, u32, u32)> = Vec::new(); for (eid_usize, &keep) in edge_set.iter().enumerate() {
91 if keep {
92 #[allow(clippy::cast_possible_truncation)]
93 let eid = eid_usize as u32;
94 let (from, to) = graph.edge(eid)?;
95 retained_edges.push((from, to, eid));
96 if delete_vertices {
97 vertex_remain[from as usize] = true;
98 vertex_remain[to as usize] = true;
99 }
100 }
101 }
102
103 let mut vertex_map: Vec<u32> = vec![u32::MAX; n as usize];
105 let mut vertex_invmap: Vec<VertexId> = Vec::new();
106 let mut new_vid: u32 = 0;
107 for vid in 0..n {
108 if vertex_remain[vid as usize] {
109 vertex_map[vid as usize] = new_vid;
110 vertex_invmap.push(vid);
111 new_vid = new_vid.checked_add(1).ok_or(IgraphError::Internal(
112 "subgraph_from_edges: vertex count overflow",
113 ))?;
114 }
115 }
116
117 let new_vcount = new_vid;
118
119 let mut result_graph = if graph.is_directed() {
121 Graph::new(new_vcount, true)?
122 } else {
123 Graph::with_vertices(new_vcount)
124 };
125
126 let mut edge_map: Vec<u32> = Vec::with_capacity(retained_edges.len());
127 for (from, to, old_eid) in &retained_edges {
128 let new_from = vertex_map[*from as usize];
129 let new_to = vertex_map[*to as usize];
130 result_graph.add_edge(new_from, new_to)?;
131 edge_map.push(*old_eid);
132 }
133
134 Ok(SubgraphFromEdgesResult {
135 graph: result_graph,
136 vertex_map,
137 vertex_invmap,
138 edge_map,
139 })
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[test]
147 fn basic_undirected() {
148 let mut g = Graph::with_vertices(5);
149 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); g.add_edge(2, 3).unwrap(); g.add_edge(3, 4).unwrap(); g.add_edge(0, 4).unwrap(); let r = subgraph_from_edges(&g, &[0, 2, 4], true).unwrap();
156 assert_eq!(r.graph.ecount(), 3);
157 assert_eq!(r.graph.vcount(), 5);
159 assert_eq!(r.edge_map, vec![0, 2, 4]);
160 }
161
162 #[test]
163 fn delete_isolated_vertices() {
164 let mut g = Graph::with_vertices(5);
165 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); g.add_edge(2, 3).unwrap(); g.add_edge(3, 4).unwrap(); let r = subgraph_from_edges(&g, &[0], true).unwrap();
172 assert_eq!(r.graph.ecount(), 1);
173 assert_eq!(r.graph.vcount(), 2);
174 assert_eq!(r.vertex_invmap, vec![0, 1]);
175 }
176
177 #[test]
178 fn keep_all_vertices() {
179 let mut g = Graph::with_vertices(5);
180 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); g.add_edge(2, 3).unwrap(); g.add_edge(3, 4).unwrap(); let r = subgraph_from_edges(&g, &[0], false).unwrap();
187 assert_eq!(r.graph.ecount(), 1);
188 assert_eq!(r.graph.vcount(), 5);
189 for i in 0..5u32 {
191 assert_eq!(r.vertex_map[i as usize], i);
192 }
193 }
194
195 #[test]
196 fn empty_edge_set() {
197 let mut g = Graph::with_vertices(3);
198 g.add_edge(0, 1).unwrap();
199 g.add_edge(1, 2).unwrap();
200
201 let r = subgraph_from_edges(&g, &[], true).unwrap();
202 assert_eq!(r.graph.ecount(), 0);
203 assert_eq!(r.graph.vcount(), 0);
204 assert!(r.vertex_invmap.is_empty());
205 }
206
207 #[test]
208 fn empty_edge_set_keep_vertices() {
209 let mut g = Graph::with_vertices(3);
210 g.add_edge(0, 1).unwrap();
211
212 let r = subgraph_from_edges(&g, &[], false).unwrap();
213 assert_eq!(r.graph.ecount(), 0);
214 assert_eq!(r.graph.vcount(), 3);
215 }
216
217 #[test]
218 fn duplicate_edge_ids() {
219 let mut g = Graph::with_vertices(3);
220 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); let r = subgraph_from_edges(&g, &[0, 0, 1, 1, 0], true).unwrap();
224 assert_eq!(r.graph.ecount(), 2);
225 assert_eq!(r.graph.vcount(), 3);
226 }
227
228 #[test]
229 fn edge_id_out_of_range() {
230 let mut g = Graph::with_vertices(3);
231 g.add_edge(0, 1).unwrap();
232
233 let err = subgraph_from_edges(&g, &[5], true).unwrap_err();
234 assert!(matches!(err, IgraphError::InvalidArgument(_)));
235 }
236
237 #[test]
238 fn directed_graph() {
239 let mut g = Graph::new(4, true).unwrap();
240 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); g.add_edge(2, 3).unwrap(); g.add_edge(3, 0).unwrap(); let r = subgraph_from_edges(&g, &[0, 2], true).unwrap();
246 assert!(r.graph.is_directed());
247 assert_eq!(r.graph.ecount(), 2);
248 assert_eq!(r.graph.vcount(), 4);
250 }
251
252 #[test]
253 fn directed_delete_vertices() {
254 let mut g = Graph::new(5, true).unwrap();
255 g.add_edge(0, 1).unwrap(); g.add_edge(2, 3).unwrap(); g.add_edge(3, 4).unwrap(); let r = subgraph_from_edges(&g, &[1], true).unwrap();
261 assert_eq!(r.graph.vcount(), 2);
262 assert_eq!(r.graph.ecount(), 1);
263 assert_eq!(r.vertex_invmap, vec![2, 3]);
264 assert_eq!(r.vertex_map[2], 0);
266 assert_eq!(r.vertex_map[3], 1);
267 }
268
269 #[test]
270 fn vertex_map_consistency() {
271 let mut g = Graph::with_vertices(6);
272 g.add_edge(1, 3).unwrap(); g.add_edge(3, 5).unwrap(); let r = subgraph_from_edges(&g, &[0, 1], true).unwrap();
276 assert_eq!(r.graph.vcount(), 3);
277 assert_eq!(r.vertex_invmap, vec![1, 3, 5]);
278 assert_eq!(r.vertex_map[1], 0);
279 assert_eq!(r.vertex_map[3], 1);
280 assert_eq!(r.vertex_map[5], 2);
281 assert_eq!(r.vertex_map[0], u32::MAX);
283 assert_eq!(r.vertex_map[2], u32::MAX);
284 assert_eq!(r.vertex_map[4], u32::MAX);
285 }
286
287 #[test]
288 fn all_edges_selected() {
289 let mut g = Graph::with_vertices(4);
290 g.add_edge(0, 1).unwrap();
291 g.add_edge(1, 2).unwrap();
292 g.add_edge(2, 3).unwrap();
293
294 let r = subgraph_from_edges(&g, &[0, 1, 2], true).unwrap();
295 assert_eq!(r.graph.vcount(), 4);
296 assert_eq!(r.graph.ecount(), 3);
297 }
298
299 #[test]
300 fn self_loop_edge() {
301 let mut g = Graph::with_vertices(3);
302 g.add_edge(0, 0).unwrap(); g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); let r = subgraph_from_edges(&g, &[0], true).unwrap();
307 assert_eq!(r.graph.vcount(), 1);
308 assert_eq!(r.graph.ecount(), 1);
309 assert_eq!(r.vertex_invmap, vec![0]);
310 }
311
312 #[test]
313 fn edge_map_ordering() {
314 let mut g = Graph::with_vertices(4);
315 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); g.add_edge(2, 3).unwrap(); let r = subgraph_from_edges(&g, &[2, 0], true).unwrap();
321 assert_eq!(r.edge_map, vec![0, 2]);
322 }
323}