rust_igraph/algorithms/
dominating_set.rs1use crate::core::{Graph, IgraphResult, VertexId};
12
13pub fn is_dominating_set(graph: &Graph, dom_set: &[VertexId]) -> bool {
37 let n = graph.vcount() as usize;
38 if n == 0 {
39 return true;
40 }
41
42 let mut dominated = vec![false; n];
43 let directed = graph.is_directed();
44
45 for &v in dom_set {
46 if (v as usize) >= n {
47 continue;
48 }
49 dominated[v as usize] = true;
50
51 if let Ok(neighbors) = graph.neighbors(v) {
52 for &w in &neighbors {
53 dominated[w as usize] = true;
54 }
55 }
56 if directed {
57 if let Ok(in_neighbors) = graph.in_neighbors_vec(v) {
58 for &w in &in_neighbors {
59 dominated[w as usize] = true;
60 }
61 }
62 }
63 }
64
65 dominated.iter().all(|&d| d)
66}
67
68#[allow(clippy::cast_possible_truncation)] pub fn minimum_dominating_set(graph: &Graph) -> IgraphResult<Vec<VertexId>> {
101 let n = graph.vcount();
102 let directed = graph.is_directed();
103
104 if n == 0 {
105 return Ok(Vec::new());
106 }
107
108 let mut dominated = vec![false; n as usize];
109 let mut undominated_count = n;
110 let mut result = Vec::new();
111
112 let mut closed_neighborhoods: Vec<Vec<VertexId>> = Vec::with_capacity(n as usize);
114 for v in 0..n {
115 let mut cn = vec![v];
116 let out_n = graph.neighbors(v)?;
117 for &w in &out_n {
118 if !cn.contains(&w) {
119 cn.push(w);
120 }
121 }
122 if directed {
123 let in_n = graph.in_neighbors_vec(v)?;
124 for &w in &in_n {
125 if !cn.contains(&w) {
126 cn.push(w);
127 }
128 }
129 }
130 closed_neighborhoods.push(cn);
131 }
132
133 while undominated_count > 0 {
134 let mut best_v = 0u32;
135 let mut best_gain = 0u32;
136
137 for v in 0..n {
138 if dominated[v as usize] {
139 }
141 let gain = closed_neighborhoods[v as usize]
142 .iter()
143 .filter(|&&w| !dominated[w as usize])
144 .count() as u32;
145 if gain > best_gain || (gain == best_gain && v < best_v) {
146 best_gain = gain;
147 best_v = v;
148 }
149 }
150
151 if best_gain == 0 {
152 break;
153 }
154
155 result.push(best_v);
156 for &w in &closed_neighborhoods[best_v as usize] {
157 if !dominated[w as usize] {
158 dominated[w as usize] = true;
159 undominated_count -= 1;
160 }
161 }
162 }
163
164 result.sort_unstable();
165 Ok(result)
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn empty_graph() {
174 let g = Graph::with_vertices(0);
175 let ds = minimum_dominating_set(&g).unwrap();
176 assert!(ds.is_empty());
177 assert!(is_dominating_set(&g, &ds));
178 }
179
180 #[test]
181 fn single_vertex() {
182 let g = Graph::with_vertices(1);
183 let ds = minimum_dominating_set(&g).unwrap();
184 assert_eq!(ds.len(), 1);
185 assert!(is_dominating_set(&g, &ds));
186 }
187
188 #[test]
189 fn no_edges() {
190 let g = Graph::with_vertices(5);
191 let ds = minimum_dominating_set(&g).unwrap();
192 assert_eq!(ds.len(), 5);
193 assert!(is_dominating_set(&g, &ds));
194 }
195
196 #[test]
197 fn single_edge() {
198 let mut g = Graph::with_vertices(2);
199 g.add_edge(0, 1).unwrap();
200 let ds = minimum_dominating_set(&g).unwrap();
201 assert_eq!(ds.len(), 1);
202 assert!(is_dominating_set(&g, &ds));
203 }
204
205 #[test]
206 fn path_5() {
207 let mut g = Graph::with_vertices(5);
208 g.add_edge(0, 1).unwrap();
209 g.add_edge(1, 2).unwrap();
210 g.add_edge(2, 3).unwrap();
211 g.add_edge(3, 4).unwrap();
212 let ds = minimum_dominating_set(&g).unwrap();
213 assert!(is_dominating_set(&g, &ds));
214 assert!(ds.len() <= 3); }
216
217 #[test]
218 fn triangle() {
219 let mut g = Graph::with_vertices(3);
220 g.add_edge(0, 1).unwrap();
221 g.add_edge(1, 2).unwrap();
222 g.add_edge(2, 0).unwrap();
223 let ds = minimum_dominating_set(&g).unwrap();
224 assert!(is_dominating_set(&g, &ds));
225 assert_eq!(ds.len(), 1);
226 }
227
228 #[test]
229 fn star_graph() {
230 let mut g = Graph::with_vertices(5);
231 for i in 1..5u32 {
232 g.add_edge(0, i).unwrap();
233 }
234 let ds = minimum_dominating_set(&g).unwrap();
235 assert!(is_dominating_set(&g, &ds));
236 assert_eq!(ds.len(), 1); }
238
239 #[test]
240 fn complete_k4() {
241 let mut g = Graph::with_vertices(4);
242 for u in 0..4u32 {
243 for v in (u + 1)..4 {
244 g.add_edge(u, v).unwrap();
245 }
246 }
247 let ds = minimum_dominating_set(&g).unwrap();
248 assert!(is_dominating_set(&g, &ds));
249 assert_eq!(ds.len(), 1);
250 }
251
252 #[test]
253 fn bipartite_k22() {
254 let mut g = Graph::with_vertices(4);
255 g.add_edge(0, 2).unwrap();
256 g.add_edge(0, 3).unwrap();
257 g.add_edge(1, 2).unwrap();
258 g.add_edge(1, 3).unwrap();
259 let ds = minimum_dominating_set(&g).unwrap();
260 assert!(is_dominating_set(&g, &ds));
261 assert!(ds.len() <= 2);
262 }
263
264 #[test]
265 fn directed_graph() {
266 let mut g = Graph::new(3, true).unwrap();
267 g.add_edge(0, 1).unwrap();
268 g.add_edge(1, 2).unwrap();
269 let ds = minimum_dominating_set(&g).unwrap();
270 assert!(is_dominating_set(&g, &ds));
271 }
272
273 #[test]
274 fn self_loop() {
275 let mut g = Graph::with_vertices(3);
276 g.add_edge(0, 0).unwrap();
277 g.add_edge(1, 2).unwrap();
278 let ds = minimum_dominating_set(&g).unwrap();
279 assert!(is_dominating_set(&g, &ds));
280 }
281
282 #[test]
283 fn isolated_with_edges() {
284 let mut g = Graph::with_vertices(5);
285 g.add_edge(0, 1).unwrap();
286 g.add_edge(2, 3).unwrap();
287 let ds = minimum_dominating_set(&g).unwrap();
289 assert!(is_dominating_set(&g, &ds));
290 assert!(ds.contains(&4)); }
292
293 #[test]
294 fn sorted_output() {
295 let mut g = Graph::with_vertices(6);
296 g.add_edge(4, 5).unwrap();
297 g.add_edge(0, 1).unwrap();
298 g.add_edge(2, 3).unwrap();
299 let ds = minimum_dominating_set(&g).unwrap();
300 for i in 1..ds.len() {
301 assert!(ds[i] > ds[i - 1]);
302 }
303 }
304
305 #[test]
306 fn validator_positive() {
307 let mut g = Graph::with_vertices(5);
308 g.add_edge(0, 1).unwrap();
309 g.add_edge(1, 2).unwrap();
310 g.add_edge(2, 3).unwrap();
311 g.add_edge(3, 4).unwrap();
312 assert!(is_dominating_set(&g, &[1, 3]));
313 assert!(is_dominating_set(&g, &[0, 2, 4]));
314 assert!(is_dominating_set(&g, &[0, 1, 2, 3, 4]));
315 }
316
317 #[test]
318 fn validator_negative() {
319 let mut g = Graph::with_vertices(5);
320 g.add_edge(0, 1).unwrap();
321 g.add_edge(1, 2).unwrap();
322 g.add_edge(2, 3).unwrap();
323 g.add_edge(3, 4).unwrap();
324 assert!(!is_dominating_set(&g, &[]));
325 assert!(!is_dominating_set(&g, &[0, 4])); assert!(!is_dominating_set(&g, &[2])); }
328
329 #[test]
330 fn petersen_graph() {
331 let mut g = Graph::with_vertices(10);
332 for i in 0..5u32 {
333 g.add_edge(i, (i + 1) % 5).unwrap();
334 }
335 for i in 0..5u32 {
336 g.add_edge(5 + i, 5 + (i + 2) % 5).unwrap();
337 }
338 for i in 0..5u32 {
339 g.add_edge(i, 5 + i).unwrap();
340 }
341 let ds = minimum_dominating_set(&g).unwrap();
342 assert!(is_dominating_set(&g, &ds));
343 assert!(ds.len() <= 5); }
346
347 #[test]
348 fn directed_domination_both_directions() {
349 let mut g = Graph::new(3, true).unwrap();
352 g.add_edge(0, 1).unwrap();
353 g.add_edge(2, 1).unwrap();
354 assert!(is_dominating_set(&g, &[1]));
355 }
356}