rust_igraph/algorithms/properties/
is_complete_multipartite.rs1use crate::algorithms::connectivity::connected_components;
11use crate::algorithms::operators::complementer::complementer;
12use crate::algorithms::properties::is_simple::is_simple;
13use crate::core::{Graph, IgraphResult};
14
15pub fn is_complete_multipartite(graph: &Graph) -> IgraphResult<Option<Vec<u32>>> {
55 if graph.is_directed() {
56 return Ok(None);
57 }
58
59 let n = graph.vcount();
60 if n == 0 {
61 return Ok(Some(vec![]));
62 }
63
64 if !is_simple(graph)? {
65 return Ok(None);
66 }
67
68 if graph.ecount() == 0 {
70 return Ok(Some(vec![n]));
71 }
72
73 let comp = complementer(graph, false)?;
75 let comps = connected_components(&comp)?;
76
77 let membership = &comps.membership;
78 let num_comps = comps.count as usize;
79
80 let mut parts: Vec<Vec<u32>> = vec![vec![]; num_comps];
82 for (v, &comp_id) in membership.iter().enumerate() {
83 parts[comp_id as usize].push(u32::try_from(v).unwrap_or(u32::MAX));
84 }
85
86 for part in &parts {
88 if part.len() > 1 {
89 let subgraph_comp = is_clique_in_graph(&comp, part)?;
91 if !subgraph_comp {
92 return Ok(None);
93 }
94 }
95 }
96
97 let mut expected_edges: u64 = 0;
99 let part_sizes: Vec<u32> = parts
100 .iter()
101 .map(|p| u32::try_from(p.len()).unwrap_or(u32::MAX))
102 .collect();
103 let total_n = u64::from(n);
104 for &size in &part_sizes {
105 let s = u64::from(size);
106 expected_edges = expected_edges.saturating_add(s.saturating_mul(total_n.saturating_sub(s)));
108 }
109 expected_edges /= 2;
111
112 if graph.ecount() as u64 != expected_edges {
113 return Ok(None);
114 }
115
116 let mut result: Vec<u32> = part_sizes;
117 result.sort_unstable();
118 Ok(Some(result))
119}
120
121fn is_clique_in_graph(graph: &Graph, vertices: &[u32]) -> IgraphResult<bool> {
122 let k = vertices.len();
123 if k <= 1 {
124 return Ok(true);
125 }
126
127 for (i, &vi) in vertices.iter().enumerate() {
128 let nbrs = graph.neighbors(vi)?;
129 let count = vertices
130 .iter()
131 .enumerate()
132 .filter(|&(j, &vj)| i != j && nbrs.contains(&vj))
133 .count();
134 if count != k - 1 {
135 return Ok(false);
136 }
137 }
138 Ok(true)
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 #[test]
146 fn empty_graph() {
147 let g = Graph::with_vertices(0);
148 assert_eq!(is_complete_multipartite(&g).unwrap(), Some(vec![]));
149 }
150
151 #[test]
152 fn single_vertex() {
153 let g = Graph::with_vertices(1);
154 assert_eq!(is_complete_multipartite(&g).unwrap(), Some(vec![1]));
155 }
156
157 #[test]
158 fn edgeless_3() {
159 let g = Graph::with_vertices(3);
160 assert_eq!(is_complete_multipartite(&g).unwrap(), Some(vec![3]));
161 }
162
163 #[test]
164 fn single_edge_k11() {
165 let mut g = Graph::with_vertices(2);
166 g.add_edge(0, 1).unwrap();
167 assert_eq!(is_complete_multipartite(&g).unwrap(), Some(vec![1, 1]));
168 }
169
170 #[test]
171 fn triangle_k111() {
172 let mut g = Graph::with_vertices(3);
173 g.add_edge(0, 1).unwrap();
174 g.add_edge(1, 2).unwrap();
175 g.add_edge(2, 0).unwrap();
176 assert_eq!(is_complete_multipartite(&g).unwrap(), Some(vec![1, 1, 1]));
177 }
178
179 #[test]
180 fn k23() {
181 let mut g = Graph::with_vertices(5);
182 for i in 0..2u32 {
183 for j in 2..5u32 {
184 g.add_edge(i, j).unwrap();
185 }
186 }
187 assert_eq!(is_complete_multipartite(&g).unwrap(), Some(vec![2, 3]));
188 }
189
190 #[test]
191 fn k22() {
192 let mut g = Graph::with_vertices(4);
193 g.add_edge(0, 2).unwrap();
194 g.add_edge(0, 3).unwrap();
195 g.add_edge(1, 2).unwrap();
196 g.add_edge(1, 3).unwrap();
197 assert_eq!(is_complete_multipartite(&g).unwrap(), Some(vec![2, 2]));
198 }
199
200 #[test]
201 fn k_1_1_1_1() {
202 let mut g = Graph::with_vertices(4);
204 g.add_edge(0, 1).unwrap();
205 g.add_edge(0, 2).unwrap();
206 g.add_edge(0, 3).unwrap();
207 g.add_edge(1, 2).unwrap();
208 g.add_edge(1, 3).unwrap();
209 g.add_edge(2, 3).unwrap();
210 assert_eq!(
211 is_complete_multipartite(&g).unwrap(),
212 Some(vec![1, 1, 1, 1])
213 );
214 }
215
216 #[test]
217 fn star_k13() {
218 let mut g = Graph::with_vertices(4);
220 g.add_edge(0, 1).unwrap();
221 g.add_edge(0, 2).unwrap();
222 g.add_edge(0, 3).unwrap();
223 assert_eq!(is_complete_multipartite(&g).unwrap(), Some(vec![1, 3]));
224 }
225
226 #[test]
227 fn k_2_2_2() {
228 let mut g = Graph::with_vertices(6);
230 let parts = [vec![0u32, 1], vec![2, 3], vec![4, 5]];
231 for i in 0..3 {
232 for j in (i + 1)..3 {
233 for &u in &parts[i] {
234 for &v in &parts[j] {
235 g.add_edge(u, v).unwrap();
236 }
237 }
238 }
239 }
240 assert_eq!(is_complete_multipartite(&g).unwrap(), Some(vec![2, 2, 2]));
241 }
242
243 #[test]
244 fn path_p3_is_k12() {
245 let mut g = Graph::with_vertices(3);
247 g.add_edge(0, 1).unwrap();
248 g.add_edge(1, 2).unwrap();
249 assert_eq!(is_complete_multipartite(&g).unwrap(), Some(vec![1, 2]));
250 }
251
252 #[test]
253 fn path_p4_not_multipartite() {
254 let mut g = Graph::with_vertices(4);
257 g.add_edge(0, 1).unwrap();
258 g.add_edge(1, 2).unwrap();
259 g.add_edge(2, 3).unwrap();
260 assert_eq!(is_complete_multipartite(&g).unwrap(), None);
261 }
262
263 #[test]
264 fn cycle_c4_is_k22() {
265 let mut g = Graph::with_vertices(4);
267 g.add_edge(0, 1).unwrap();
268 g.add_edge(1, 2).unwrap();
269 g.add_edge(2, 3).unwrap();
270 g.add_edge(3, 0).unwrap();
271 assert_eq!(is_complete_multipartite(&g).unwrap(), Some(vec![2, 2]));
272 }
273
274 #[test]
275 fn cycle_c5_not_multipartite() {
276 let mut g = Graph::with_vertices(5);
277 g.add_edge(0, 1).unwrap();
278 g.add_edge(1, 2).unwrap();
279 g.add_edge(2, 3).unwrap();
280 g.add_edge(3, 4).unwrap();
281 g.add_edge(4, 0).unwrap();
282 assert_eq!(is_complete_multipartite(&g).unwrap(), None);
283 }
284
285 #[test]
286 fn directed_returns_none() {
287 let mut g = Graph::new(3, true).unwrap();
288 g.add_edge(0, 1).unwrap();
289 g.add_edge(1, 2).unwrap();
290 g.add_edge(2, 0).unwrap();
291 assert_eq!(is_complete_multipartite(&g).unwrap(), None);
292 }
293
294 #[test]
295 fn disconnected_not_multipartite() {
296 let mut g = Graph::with_vertices(4);
297 g.add_edge(0, 1).unwrap();
298 g.add_edge(2, 3).unwrap();
299 assert_eq!(is_complete_multipartite(&g).unwrap(), None);
300 }
301
302 #[test]
303 fn petersen_not_multipartite() {
304 let mut g = Graph::with_vertices(10);
306 for i in 0..5u32 {
308 g.add_edge(i, (i + 1) % 5).unwrap();
309 }
310 for i in 0..5u32 {
312 g.add_edge(i + 5, 5 + (i + 2) % 5).unwrap();
313 }
314 for i in 0..5u32 {
316 g.add_edge(i, i + 5).unwrap();
317 }
318 assert_eq!(is_complete_multipartite(&g).unwrap(), None);
319 }
320}