rust_igraph/algorithms/properties/
merrifield_simmons.rs1#![allow(
12 clippy::cast_possible_truncation,
13 clippy::cast_precision_loss,
14 clippy::many_single_char_names,
15 clippy::needless_range_loop,
16 clippy::too_many_lines
17)]
18
19use crate::core::{Graph, IgraphResult};
20
21pub fn merrifield_simmons_index(graph: &Graph) -> IgraphResult<u64> {
38 let seq = independent_set_count_sequence(graph)?;
39 let total: u64 = seq.iter().sum();
40 Ok(total)
41}
42
43pub fn independent_set_count_sequence(graph: &Graph) -> IgraphResult<Vec<u64>> {
59 let n = graph.vcount() as usize;
60
61 let mut counts = vec![0_u64; n.saturating_add(1)];
62 counts[0] = 1;
63
64 if n == 0 {
65 return Ok(counts);
66 }
67
68 let adj = build_adj_matrix(graph, n);
69
70 let mut selected = vec![false; n];
71 enumerate_independent_sets(&adj, n, 0, 0, &mut selected, &mut counts);
72
73 while counts.len() > 1 && counts[counts.len() - 1] == 0 {
74 counts.pop();
75 }
76
77 Ok(counts)
78}
79
80fn build_adj_matrix(graph: &Graph, n: usize) -> Vec<bool> {
81 let mut adj = vec![false; n * n];
82 for (u, v) in graph.edges() {
83 let ui = u as usize;
84 let vi = v as usize;
85 if ui != vi {
86 adj[ui * n + vi] = true;
87 if !graph.is_directed() {
88 adj[vi * n + ui] = true;
89 }
90 }
91 }
92 adj
93}
94
95fn enumerate_independent_sets(
96 adj: &[bool],
97 n: usize,
98 start: usize,
99 k: usize,
100 selected: &mut [bool],
101 counts: &mut [u64],
102) {
103 for v in start..n {
104 let mut conflict = false;
105 for u in 0..v {
106 if selected[u] && adj[u * n + v] {
107 conflict = true;
108 break;
109 }
110 }
111 if conflict {
112 continue;
113 }
114
115 selected[v] = true;
116 let new_k = k.saturating_add(1);
117 if new_k < counts.len() {
118 counts[new_k] = counts[new_k].saturating_add(1);
119 enumerate_independent_sets(adj, n, v.saturating_add(1), new_k, selected, counts);
120 }
121 selected[v] = false;
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 fn empty() -> Graph {
130 Graph::with_vertices(0)
131 }
132
133 fn single() -> Graph {
134 Graph::with_vertices(1)
135 }
136
137 fn no_edges3() -> Graph {
138 Graph::with_vertices(3)
139 }
140
141 fn single_edge() -> Graph {
142 Graph::from_edges(&[(0, 1)], false, Some(2)).unwrap()
143 }
144
145 fn path3() -> Graph {
146 Graph::from_edges(&[(0, 1), (1, 2)], false, Some(3)).unwrap()
147 }
148
149 fn path4() -> Graph {
150 Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], false, Some(4)).unwrap()
151 }
152
153 fn k3() -> Graph {
154 Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], false, Some(3)).unwrap()
155 }
156
157 fn k4() -> Graph {
158 Graph::from_edges(
159 &[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)],
160 false,
161 Some(4),
162 )
163 .unwrap()
164 }
165
166 fn cycle4() -> Graph {
167 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], false, Some(4)).unwrap()
168 }
169
170 fn cycle5() -> Graph {
171 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], false, Some(5)).unwrap()
172 }
173
174 fn star4() -> Graph {
175 Graph::from_edges(&[(0, 1), (0, 2), (0, 3)], false, Some(4)).unwrap()
176 }
177
178 #[test]
181 fn msi_empty() {
182 assert_eq!(merrifield_simmons_index(&empty()).unwrap(), 1);
184 }
185
186 #[test]
187 fn msi_single() {
188 assert_eq!(merrifield_simmons_index(&single()).unwrap(), 2);
190 }
191
192 #[test]
193 fn msi_no_edges() {
194 assert_eq!(merrifield_simmons_index(&no_edges3()).unwrap(), 8);
196 }
197
198 #[test]
199 fn msi_single_edge() {
200 assert_eq!(merrifield_simmons_index(&single_edge()).unwrap(), 3);
202 }
203
204 #[test]
205 fn msi_path3() {
206 assert_eq!(merrifield_simmons_index(&path3()).unwrap(), 5);
208 }
209
210 #[test]
211 fn msi_path4() {
212 assert_eq!(merrifield_simmons_index(&path4()).unwrap(), 8);
215 }
216
217 #[test]
218 fn msi_k3() {
219 assert_eq!(merrifield_simmons_index(&k3()).unwrap(), 4);
221 }
222
223 #[test]
224 fn msi_k4() {
225 assert_eq!(merrifield_simmons_index(&k4()).unwrap(), 5);
227 }
228
229 #[test]
230 fn msi_cycle4() {
231 assert_eq!(merrifield_simmons_index(&cycle4()).unwrap(), 7);
233 }
234
235 #[test]
236 fn msi_cycle5() {
237 assert_eq!(merrifield_simmons_index(&cycle5()).unwrap(), 11);
240 }
241
242 #[test]
243 fn msi_star4() {
244 assert_eq!(merrifield_simmons_index(&star4()).unwrap(), 9);
246 }
247
248 #[test]
251 fn iscs_empty() {
252 assert_eq!(independent_set_count_sequence(&empty()).unwrap(), vec![1]);
253 }
254
255 #[test]
256 fn iscs_single() {
257 assert_eq!(
258 independent_set_count_sequence(&single()).unwrap(),
259 vec![1, 1]
260 );
261 }
262
263 #[test]
264 fn iscs_no_edges() {
265 assert_eq!(
267 independent_set_count_sequence(&no_edges3()).unwrap(),
268 vec![1, 3, 3, 1]
269 );
270 }
271
272 #[test]
273 fn iscs_single_edge() {
274 assert_eq!(
275 independent_set_count_sequence(&single_edge()).unwrap(),
276 vec![1, 2]
277 );
278 }
279
280 #[test]
281 fn iscs_path3() {
282 assert_eq!(
284 independent_set_count_sequence(&path3()).unwrap(),
285 vec![1, 3, 1]
286 );
287 }
288
289 #[test]
290 fn iscs_k3() {
291 assert_eq!(independent_set_count_sequence(&k3()).unwrap(), vec![1, 3]);
292 }
293
294 #[test]
295 fn iscs_k4() {
296 assert_eq!(independent_set_count_sequence(&k4()).unwrap(), vec![1, 4]);
297 }
298
299 #[test]
300 fn iscs_cycle4() {
301 assert_eq!(
303 independent_set_count_sequence(&cycle4()).unwrap(),
304 vec![1, 4, 2]
305 );
306 }
307
308 #[test]
309 fn iscs_cycle5() {
310 assert_eq!(
312 independent_set_count_sequence(&cycle5()).unwrap(),
313 vec![1, 5, 5]
314 );
315 }
316
317 #[test]
320 fn msi_equals_sum_of_sequence() {
321 for g in &[path3(), path4(), k3(), k4(), cycle4(), cycle5(), star4()] {
322 let sigma = merrifield_simmons_index(g).unwrap();
323 let seq = independent_set_count_sequence(g).unwrap();
324 let sum: u64 = seq.iter().sum();
325 assert_eq!(sigma, sum);
326 }
327 }
328
329 #[test]
330 fn i0_always_one() {
331 for g in &[empty(), single(), no_edges3(), path4(), k4()] {
332 let seq = independent_set_count_sequence(g).unwrap();
333 assert_eq!(seq[0], 1);
334 }
335 }
336
337 #[test]
338 fn i1_equals_vertex_count() {
339 for g in &[single(), single_edge(), path3(), k3(), k4(), cycle4()] {
340 let seq = independent_set_count_sequence(g).unwrap();
341 if seq.len() > 1 {
342 assert_eq!(seq[1], u64::from(g.vcount()));
343 }
344 }
345 }
346
347 #[test]
348 fn no_edges_is_power_of_two() {
349 for n in 0_u32..=5 {
350 let g = Graph::with_vertices(n);
351 let sigma = merrifield_simmons_index(&g).unwrap();
352 assert_eq!(sigma, 1_u64 << n);
353 }
354 }
355
356 #[test]
357 fn complete_graph_sigma() {
358 for n in 1_u32..=5 {
360 let edges: Vec<(u32, u32)> = (0..n)
361 .flat_map(|i| ((i + 1)..n).map(move |j| (i, j)))
362 .collect();
363 let g = if edges.is_empty() {
364 Graph::with_vertices(n)
365 } else {
366 Graph::from_edges(&edges, false, Some(n)).unwrap()
367 };
368 assert_eq!(
369 merrifield_simmons_index(&g).unwrap(),
370 u64::from(n) + 1,
371 "σ(K_{n}) should be {}",
372 n + 1
373 );
374 }
375 }
376
377 #[test]
378 fn path_sigma_fibonacci() {
379 let fib = [1, 1, 2, 3, 5, 8, 13, 21];
381 for n in 1_u32..=6 {
382 let edges: Vec<(u32, u32)> = (0..n.saturating_sub(1)).map(|i| (i, i + 1)).collect();
383 let g = if n == 1 {
384 Graph::with_vertices(1)
385 } else {
386 Graph::from_edges(&edges, false, Some(n)).unwrap()
387 };
388 assert_eq!(
389 merrifield_simmons_index(&g).unwrap(),
390 fib[(n + 1) as usize],
391 "σ(P_{n}) should be F({})",
392 n + 2
393 );
394 }
395 }
396}