rust_igraph/algorithms/operators/
simplify.rs1use crate::core::graph::EdgeId;
18use crate::core::{Graph, IgraphResult, VertexId};
19
20pub fn simplify(graph: &Graph, remove_multiple: bool, remove_loops: bool) -> IgraphResult<Graph> {
48 let n = graph.vcount();
49 let directed = graph.is_directed();
50
51 if !remove_multiple && !remove_loops {
52 return Ok(graph.clone());
53 }
54
55 let m = graph.ecount();
56 let mut edges: Vec<(VertexId, VertexId)> = Vec::with_capacity(m);
57 for eid in 0..m {
58 let eid_u32 = u32::try_from(eid)
59 .map_err(|_| crate::IgraphError::Internal("edge id exceeds u32::MAX"))?;
60 let (f, t) = graph.edge(eid_u32 as EdgeId)?;
61 if remove_loops && f == t {
62 continue;
63 }
64 edges.push((f, t));
65 }
66
67 if remove_multiple {
68 edges.sort_unstable();
75 edges.dedup();
76 }
77
78 let mut g = Graph::new(n, directed)?;
79 g.add_edges(edges)?;
80 Ok(g)
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 fn sorted_edges(g: &Graph) -> Vec<(VertexId, VertexId)> {
88 let m = u32::try_from(g.ecount()).expect("ecount fits in u32");
89 let mut v: Vec<_> = (0..m).map(|e| g.edge(e).unwrap()).collect();
90 v.sort_unstable();
91 v
92 }
93
94 #[test]
95 fn no_op_when_both_flags_false() {
96 let mut g = Graph::with_vertices(3);
97 g.add_edge(0, 0).unwrap();
98 g.add_edge(0, 1).unwrap();
99 g.add_edge(0, 1).unwrap();
100 let s = simplify(&g, false, false).unwrap();
101 assert_eq!(s.vcount(), g.vcount());
102 assert_eq!(s.ecount(), g.ecount());
103 assert_eq!(sorted_edges(&s), sorted_edges(&g));
104 }
105
106 #[test]
107 fn removes_self_loops_only() {
108 let mut g = Graph::with_vertices(3);
109 g.add_edge(0, 0).unwrap();
110 g.add_edge(1, 1).unwrap();
111 g.add_edge(0, 1).unwrap();
112 g.add_edge(0, 1).unwrap();
113
114 let s = simplify(&g, false, true).unwrap();
115 assert_eq!(s.ecount(), 2);
116 assert_eq!(sorted_edges(&s), vec![(0, 1), (0, 1)]);
117 }
118
119 #[test]
120 fn removes_multi_edges_only_undirected() {
121 let mut g = Graph::with_vertices(3);
122 g.add_edge(0, 1).unwrap();
123 g.add_edge(0, 1).unwrap();
124 g.add_edge(1, 0).unwrap();
125 g.add_edge(1, 2).unwrap();
126
127 let s = simplify(&g, true, false).unwrap();
128 assert_eq!(s.ecount(), 2);
129 assert_eq!(sorted_edges(&s), vec![(0, 1), (1, 2)]);
130 }
131
132 #[test]
133 fn removes_multi_edges_directed() {
134 let mut g = Graph::new(3, true).unwrap();
136 g.add_edge(0, 1).unwrap();
137 g.add_edge(0, 1).unwrap();
138 g.add_edge(1, 0).unwrap();
139 g.add_edge(1, 2).unwrap();
140
141 let s = simplify(&g, true, false).unwrap();
142 assert!(s.is_directed());
143 assert_eq!(s.ecount(), 3);
144 assert_eq!(sorted_edges(&s), vec![(0, 1), (1, 0), (1, 2)]);
145 }
146
147 #[test]
148 fn removes_both_loops_and_multi() {
149 let mut g = Graph::with_vertices(3);
150 g.add_edge(0, 0).unwrap();
151 g.add_edge(0, 1).unwrap();
152 g.add_edge(0, 1).unwrap();
153 g.add_edge(1, 2).unwrap();
154 g.add_edge(2, 0).unwrap();
155
156 let s = simplify(&g, true, true).unwrap();
157 assert_eq!(s.ecount(), 3);
158 assert_eq!(sorted_edges(&s), vec![(0, 1), (0, 2), (1, 2)]);
159 }
160
161 #[test]
162 fn idempotent_on_simple_graph() {
163 let mut g = Graph::with_vertices(4);
164 g.add_edge(0, 1).unwrap();
165 g.add_edge(1, 2).unwrap();
166 g.add_edge(2, 3).unwrap();
167 let s = simplify(&g, true, true).unwrap();
168 assert_eq!(s.ecount(), g.ecount());
169 assert_eq!(sorted_edges(&s), sorted_edges(&g));
170 }
171
172 #[test]
173 fn empty_graph() {
174 let g = Graph::with_vertices(0);
175 let s = simplify(&g, true, true).unwrap();
176 assert_eq!(s.vcount(), 0);
177 assert_eq!(s.ecount(), 0);
178 }
179
180 #[test]
181 fn vertices_preserved_when_no_edges_remain() {
182 let mut g = Graph::with_vertices(3);
183 g.add_edge(0, 0).unwrap();
184 g.add_edge(1, 1).unwrap();
185 let s = simplify(&g, true, true).unwrap();
186 assert_eq!(s.vcount(), 3);
187 assert_eq!(s.ecount(), 0);
188 }
189
190 #[test]
191 fn igraph_simplify_example_directed_5_parallel() {
192 let mut g = Graph::new(2, true).unwrap();
195 for _ in 0..5 {
196 g.add_edge(0, 1).unwrap();
197 }
198 let s = simplify(&g, true, true).unwrap();
199 assert_eq!(s.ecount(), 1);
200 assert_eq!(s.edge(0).unwrap(), (0, 1));
201 }
202
203 #[test]
204 fn igraph_simplify_example_directed_loops_and_multi() {
205 let mut g = Graph::new(3, true).unwrap();
208 g.add_edge(0, 0).unwrap();
209 g.add_edge(1, 1).unwrap();
210 g.add_edge(2, 2).unwrap();
211 g.add_edge(1, 2).unwrap();
212 let s = simplify(&g, true, true).unwrap();
213 assert_eq!(s.ecount(), 1);
214 assert_eq!(s.edge(0).unwrap(), (1, 2));
215 }
216}