rust_igraph/algorithms/operators/
intersection.rs1use std::collections::BTreeMap;
21
22use crate::core::graph::EdgeId;
23use crate::core::{Graph, IgraphError, IgraphResult, VertexId};
24
25pub fn intersection(left: &Graph, right: &Graph) -> IgraphResult<Graph> {
63 if left.is_directed() != right.is_directed() {
64 return Err(IgraphError::InvalidArgument(
65 "intersection: cannot mix directed and undirected graphs".to_string(),
66 ));
67 }
68 let directed = left.is_directed();
69 let n = std::cmp::max(left.vcount(), right.vcount());
70
71 let canon = |u: VertexId, v: VertexId| -> (VertexId, VertexId) {
72 if directed || u <= v { (u, v) } else { (v, u) }
73 };
74
75 let mut count_left: BTreeMap<(VertexId, VertexId), u32> = BTreeMap::new();
76 let mut count_right: BTreeMap<(VertexId, VertexId), u32> = BTreeMap::new();
77
78 let m_l = u32::try_from(left.ecount())
79 .map_err(|_| IgraphError::Internal("ecount exceeds u32::MAX"))?;
80 for e in 0..m_l {
81 let (u, v) = left.edge(e as EdgeId)?;
82 *count_left.entry(canon(u, v)).or_insert(0) += 1;
83 }
84 let m_r = u32::try_from(right.ecount())
85 .map_err(|_| IgraphError::Internal("ecount exceeds u32::MAX"))?;
86 for e in 0..m_r {
87 let (u, v) = right.edge(e as EdgeId)?;
88 *count_right.entry(canon(u, v)).or_insert(0) += 1;
89 }
90
91 let mut edges: Vec<(VertexId, VertexId)> = Vec::new();
96 let (driver, lookup) = if count_left.len() <= count_right.len() {
97 (&count_left, &count_right)
98 } else {
99 (&count_right, &count_left)
100 };
101 for (k, &cd) in driver {
102 if let Some(&co) = lookup.get(k) {
103 let m = std::cmp::min(cd, co);
104 for _ in 0..m {
105 edges.push(*k);
106 }
107 }
108 }
109 edges.sort_unstable();
113
114 let mut out = Graph::new(n, directed)?;
115 out.add_edges(edges)?;
116 Ok(out)
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 fn sorted_edges(g: &Graph) -> Vec<(VertexId, VertexId)> {
124 let m = u32::try_from(g.ecount()).unwrap();
125 let mut v: Vec<_> = (0..m).map(|e| g.edge(e).unwrap()).collect();
126 v.sort_unstable();
127 v
128 }
129
130 #[test]
131 fn empty_intersect_empty() {
132 let a = Graph::with_vertices(0);
133 let b = Graph::with_vertices(0);
134 let i = intersection(&a, &b).unwrap();
135 assert_eq!(i.vcount(), 0);
136 assert_eq!(i.ecount(), 0);
137 assert!(!i.is_directed());
138 }
139
140 #[test]
141 fn vcount_is_max_of_inputs() {
142 let a = Graph::with_vertices(3);
143 let b = Graph::with_vertices(7);
144 let i = intersection(&a, &b).unwrap();
145 assert_eq!(i.vcount(), 7);
146 assert_eq!(i.ecount(), 0);
147 }
148
149 #[test]
150 fn triangle_intersect_path_doc_example() {
151 let mut a = Graph::with_vertices(3);
152 a.add_edge(0, 1).unwrap();
153 a.add_edge(1, 2).unwrap();
154 a.add_edge(2, 0).unwrap();
155 let mut b = Graph::with_vertices(3);
156 b.add_edge(0, 1).unwrap();
157 b.add_edge(1, 2).unwrap();
158 let i = intersection(&a, &b).unwrap();
159 assert_eq!(i.vcount(), 3);
160 assert_eq!(i.ecount(), 2);
161 assert_eq!(sorted_edges(&i), vec![(0, 1), (1, 2)]);
162 }
163
164 #[test]
165 fn min_multiplicity_when_left_has_more() {
166 let mut a = Graph::with_vertices(2);
168 a.add_edge(0, 1).unwrap();
169 a.add_edge(0, 1).unwrap();
170 a.add_edge(0, 1).unwrap();
171 let mut b = Graph::with_vertices(2);
172 b.add_edge(0, 1).unwrap();
173 let i = intersection(&a, &b).unwrap();
174 assert_eq!(i.ecount(), 1);
175 }
176
177 #[test]
178 fn min_multiplicity_when_right_has_more() {
179 let mut a = Graph::with_vertices(2);
181 a.add_edge(0, 1).unwrap();
182 a.add_edge(0, 1).unwrap();
183 let mut b = Graph::with_vertices(2);
184 for _ in 0..5 {
185 b.add_edge(0, 1).unwrap();
186 }
187 let i = intersection(&a, &b).unwrap();
188 assert_eq!(i.ecount(), 2);
189 }
190
191 #[test]
192 fn disjoint_edge_sets_yields_empty() {
193 let mut a = Graph::with_vertices(4);
195 a.add_edge(0, 1).unwrap();
196 let mut b = Graph::with_vertices(4);
197 b.add_edge(2, 3).unwrap();
198 let i = intersection(&a, &b).unwrap();
199 assert_eq!(i.vcount(), 4);
200 assert_eq!(i.ecount(), 0);
201 }
202
203 #[test]
204 fn idempotent_with_self() {
205 let mut a = Graph::with_vertices(4);
207 a.add_edge(0, 1).unwrap();
208 a.add_edge(1, 2).unwrap();
209 a.add_edge(0, 2).unwrap();
210 a.add_edge(0, 2).unwrap(); let i = intersection(&a, &a).unwrap();
212 assert_eq!(i.vcount(), a.vcount());
213 assert_eq!(i.ecount(), a.ecount());
214 assert_eq!(sorted_edges(&i), sorted_edges(&a));
215 }
216
217 #[test]
218 fn directed_keeps_orientation_separate() {
219 let mut a = Graph::new(2, true).unwrap();
221 a.add_edge(0, 1).unwrap();
222 a.add_edge(1, 0).unwrap();
223 let mut b = Graph::new(2, true).unwrap();
224 b.add_edge(0, 1).unwrap();
225 let i = intersection(&a, &b).unwrap();
226 assert!(i.is_directed());
227 assert_eq!(i.ecount(), 1);
228 assert_eq!(i.edge(0).unwrap(), (0, 1));
229 }
230
231 #[test]
232 fn directed_min_multiplicity_per_orientation() {
233 let mut a = Graph::new(2, true).unwrap();
236 for _ in 0..3 {
237 a.add_edge(0, 1).unwrap();
238 }
239 for _ in 0..2 {
240 a.add_edge(1, 0).unwrap();
241 }
242 let mut b = Graph::new(2, true).unwrap();
243 for _ in 0..2 {
244 b.add_edge(0, 1).unwrap();
245 }
246 for _ in 0..4 {
247 b.add_edge(1, 0).unwrap();
248 }
249 let i = intersection(&a, &b).unwrap();
250 assert_eq!(i.ecount(), 4);
251 let s = sorted_edges(&i);
252 assert_eq!(s.iter().filter(|&&p| p == (0, 1)).count(), 2);
253 assert_eq!(s.iter().filter(|&&p| p == (1, 0)).count(), 2);
254 }
255
256 #[test]
257 fn loops_are_preserved_with_min_multiplicity() {
258 let mut a = Graph::with_vertices(1);
260 for _ in 0..3 {
261 a.add_edge(0, 0).unwrap();
262 }
263 let mut b = Graph::with_vertices(1);
264 b.add_edge(0, 0).unwrap();
265 let i = intersection(&a, &b).unwrap();
266 assert_eq!(i.ecount(), 1);
267 assert_eq!(i.edge(0).unwrap(), (0, 0));
268 }
269
270 #[test]
271 fn unaligned_vertex_sizes_use_max() {
272 let mut a = Graph::with_vertices(2);
274 a.add_edge(0, 1).unwrap();
275 let mut b = Graph::with_vertices(5);
276 b.add_edge(3, 4).unwrap();
277 let i = intersection(&a, &b).unwrap();
278 assert_eq!(i.vcount(), 5);
279 assert_eq!(i.ecount(), 0);
280 }
281
282 #[test]
283 fn mixed_directedness_errors() {
284 let a = Graph::with_vertices(2);
285 let b = Graph::new(2, true).unwrap();
286 assert!(intersection(&a, &b).is_err());
287 }
288
289 #[test]
290 fn undirected_canonicalises_swapped_endpoints() {
291 let mut a = Graph::with_vertices(2);
294 a.add_edge(1, 0).unwrap();
295 let mut b = Graph::with_vertices(2);
296 b.add_edge(0, 1).unwrap();
297 let i = intersection(&a, &b).unwrap();
298 assert_eq!(i.ecount(), 1);
299 }
300
301 #[test]
302 fn order_independent() {
303 let mut a = Graph::with_vertices(4);
306 a.add_edge(0, 1).unwrap();
307 a.add_edge(0, 1).unwrap();
308 a.add_edge(1, 2).unwrap();
309 a.add_edge(2, 3).unwrap();
310 let mut b = Graph::with_vertices(4);
311 b.add_edge(0, 1).unwrap();
312 b.add_edge(1, 2).unwrap();
313 b.add_edge(1, 2).unwrap();
314 let ab = intersection(&a, &b).unwrap();
315 let ba = intersection(&b, &a).unwrap();
316 assert_eq!(sorted_edges(&ab), sorted_edges(&ba));
317 }
318}