1use crate::graph_types::SimpleGraph;
7use crate::traits::{ConstraintSatisfactionProblem, Problem};
8use crate::types::{EnergyMode, LocalConstraint, LocalSolutionSize, ProblemSize, SolutionSize};
9use petgraph::graph::{NodeIndex, UnGraph};
10use serde::{Deserialize, Serialize};
11use std::collections::HashSet;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct DominatingSet<W = i32> {
37 graph: UnGraph<(), ()>,
39 weights: Vec<W>,
41}
42
43impl<W: Clone + Default> DominatingSet<W> {
44 pub fn new(num_vertices: usize, edges: Vec<(usize, usize)>) -> Self
46 where
47 W: From<i32>,
48 {
49 let mut graph = UnGraph::new_undirected();
50 for _ in 0..num_vertices {
51 graph.add_node(());
52 }
53 for (u, v) in edges {
54 graph.add_edge(NodeIndex::new(u), NodeIndex::new(v), ());
55 }
56 let weights = vec![W::from(1); num_vertices];
57 Self { graph, weights }
58 }
59
60 pub fn with_weights(num_vertices: usize, edges: Vec<(usize, usize)>, weights: Vec<W>) -> Self {
62 assert_eq!(weights.len(), num_vertices);
63 let mut graph = UnGraph::new_undirected();
64 for _ in 0..num_vertices {
65 graph.add_node(());
66 }
67 for (u, v) in edges {
68 graph.add_edge(NodeIndex::new(u), NodeIndex::new(v), ());
69 }
70 Self { graph, weights }
71 }
72
73 pub fn num_vertices(&self) -> usize {
75 self.graph.node_count()
76 }
77
78 pub fn num_edges(&self) -> usize {
80 self.graph.edge_count()
81 }
82
83 pub fn neighbors(&self, v: usize) -> Vec<usize> {
85 self.graph
86 .neighbors(NodeIndex::new(v))
87 .map(|n| n.index())
88 .collect()
89 }
90
91 pub fn closed_neighborhood(&self, v: usize) -> HashSet<usize> {
93 let mut neighborhood: HashSet<usize> = self.neighbors(v).into_iter().collect();
94 neighborhood.insert(v);
95 neighborhood
96 }
97
98 fn is_dominating(&self, config: &[usize]) -> bool {
100 let n = self.graph.node_count();
101 let mut dominated = vec![false; n];
102
103 for (v, &selected) in config.iter().enumerate() {
104 if selected == 1 {
105 dominated[v] = true;
107 for neighbor in self.neighbors(v) {
109 if neighbor < n {
110 dominated[neighbor] = true;
111 }
112 }
113 }
114 }
115
116 dominated.iter().all(|&d| d)
117 }
118}
119
120impl<W> Problem for DominatingSet<W>
121where
122 W: Clone + Default + PartialOrd + num_traits::Num + num_traits::Zero + std::ops::AddAssign + 'static,
123{
124 const NAME: &'static str = "DominatingSet";
125 type GraphType = SimpleGraph;
126 type Weight = W;
127 type Size = W;
128
129 fn num_variables(&self) -> usize {
130 self.graph.node_count()
131 }
132
133 fn num_flavors(&self) -> usize {
134 2
135 }
136
137 fn problem_size(&self) -> ProblemSize {
138 ProblemSize::new(vec![
139 ("num_vertices", self.graph.node_count()),
140 ("num_edges", self.graph.edge_count()),
141 ])
142 }
143
144 fn energy_mode(&self) -> EnergyMode {
145 EnergyMode::SmallerSizeIsBetter }
147
148 fn solution_size(&self, config: &[usize]) -> SolutionSize<Self::Size> {
149 let is_valid = self.is_dominating(config);
150 let mut total = W::zero();
151 for (i, &selected) in config.iter().enumerate() {
152 if selected == 1 {
153 total += self.weights[i].clone();
154 }
155 }
156 SolutionSize::new(total, is_valid)
157 }
158}
159
160impl<W> ConstraintSatisfactionProblem for DominatingSet<W>
161where
162 W: Clone + Default + PartialOrd + num_traits::Num + num_traits::Zero + std::ops::AddAssign + 'static,
163{
164 fn constraints(&self) -> Vec<LocalConstraint> {
165 (0..self.graph.node_count())
167 .map(|v| {
168 let closed_nbhd: Vec<usize> = self.closed_neighborhood(v).into_iter().collect();
169 let num_vars = closed_nbhd.len();
170 let num_configs = 2usize.pow(num_vars as u32);
171
172 let mut spec = vec![true; num_configs];
174 spec[0] = false;
175
176 LocalConstraint::new(2, closed_nbhd, spec)
177 })
178 .collect()
179 }
180
181 fn objectives(&self) -> Vec<LocalSolutionSize<Self::Size>> {
182 self.weights
183 .iter()
184 .enumerate()
185 .map(|(i, w)| LocalSolutionSize::new(2, vec![i], vec![W::zero(), w.clone()]))
186 .collect()
187 }
188
189 fn weights(&self) -> Vec<Self::Size> {
190 self.weights.clone()
191 }
192
193 fn set_weights(&mut self, weights: Vec<Self::Size>) {
194 assert_eq!(weights.len(), self.num_variables());
195 self.weights = weights;
196 }
197
198 fn is_weighted(&self) -> bool {
199 if self.weights.is_empty() {
200 return false;
201 }
202 let first = &self.weights[0];
203 !self.weights.iter().all(|w| w == first)
204 }
205}
206
207pub fn is_dominating_set(num_vertices: usize, edges: &[(usize, usize)], selected: &[bool]) -> bool {
209 if selected.len() != num_vertices {
210 return false;
211 }
212
213 let mut adj: Vec<HashSet<usize>> = vec![HashSet::new(); num_vertices];
215 for &(u, v) in edges {
216 if u < num_vertices && v < num_vertices {
217 adj[u].insert(v);
218 adj[v].insert(u);
219 }
220 }
221
222 for v in 0..num_vertices {
224 if selected[v] {
225 continue; }
227 let dominated = adj[v].iter().any(|&u| selected[u]);
229 if !dominated {
230 return false;
231 }
232 }
233
234 true
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240 use crate::solvers::{BruteForce, Solver};
241
242 #[test]
243 fn test_dominating_set_creation() {
244 let problem = DominatingSet::<i32>::new(4, vec![(0, 1), (1, 2), (2, 3)]);
245 assert_eq!(problem.num_vertices(), 4);
246 assert_eq!(problem.num_edges(), 3);
247 }
248
249 #[test]
250 fn test_dominating_set_with_weights() {
251 let problem = DominatingSet::with_weights(3, vec![(0, 1)], vec![1, 2, 3]);
252 assert_eq!(problem.weights(), vec![1, 2, 3]);
253 }
254
255 #[test]
256 fn test_neighbors() {
257 let problem = DominatingSet::<i32>::new(4, vec![(0, 1), (0, 2), (1, 2)]);
258 let nbrs = problem.neighbors(0);
259 assert!(nbrs.contains(&1));
260 assert!(nbrs.contains(&2));
261 assert!(!nbrs.contains(&3));
262 }
263
264 #[test]
265 fn test_closed_neighborhood() {
266 let problem = DominatingSet::<i32>::new(4, vec![(0, 1), (0, 2)]);
267 let cn = problem.closed_neighborhood(0);
268 assert!(cn.contains(&0));
269 assert!(cn.contains(&1));
270 assert!(cn.contains(&2));
271 assert!(!cn.contains(&3));
272 }
273
274 #[test]
275 fn test_solution_size_valid() {
276 let problem = DominatingSet::<i32>::new(4, vec![(0, 1), (0, 2), (0, 3)]);
278
279 let sol = problem.solution_size(&[1, 0, 0, 0]);
281 assert!(sol.is_valid);
282 assert_eq!(sol.size, 1);
283
284 let sol = problem.solution_size(&[0, 1, 1, 1]);
286 assert!(sol.is_valid);
287 assert_eq!(sol.size, 3);
288 }
289
290 #[test]
291 fn test_solution_size_invalid() {
292 let problem = DominatingSet::<i32>::new(4, vec![(0, 1), (2, 3)]);
293
294 let sol = problem.solution_size(&[0, 0, 0, 0]);
296 assert!(!sol.is_valid);
297
298 let sol = problem.solution_size(&[1, 0, 0, 0]);
300 assert!(!sol.is_valid);
301 }
302
303 #[test]
304 fn test_brute_force_star() {
305 let problem = DominatingSet::<i32>::new(4, vec![(0, 1), (0, 2), (0, 3)]);
307 let solver = BruteForce::new();
308
309 let solutions = solver.find_best(&problem);
310 assert!(solutions.contains(&vec![1, 0, 0, 0]));
311 for sol in &solutions {
312 assert_eq!(problem.solution_size(sol).size, 1);
313 }
314 }
315
316 #[test]
317 fn test_brute_force_path() {
318 let problem = DominatingSet::<i32>::new(5, vec![(0, 1), (1, 2), (2, 3), (3, 4)]);
320 let solver = BruteForce::new();
321
322 let solutions = solver.find_best(&problem);
323 for sol in &solutions {
325 assert_eq!(problem.solution_size(sol).size, 2);
326 assert!(problem.solution_size(sol).is_valid);
327 }
328 }
329
330 #[test]
331 fn test_brute_force_weighted() {
332 let problem =
334 DominatingSet::with_weights(4, vec![(0, 1), (0, 2), (0, 3)], vec![100, 1, 1, 1]);
335 let solver = BruteForce::new();
336
337 let solutions = solver.find_best(&problem);
338 assert_eq!(solutions.len(), 1);
340 assert_eq!(solutions[0], vec![0, 1, 1, 1]);
341 }
342
343 #[test]
344 fn test_is_dominating_set_function() {
345 let edges = vec![(0, 1), (0, 2), (0, 3)];
346
347 assert!(is_dominating_set(4, &edges, &[true, false, false, false]));
349 assert!(is_dominating_set(4, &edges, &[false, true, true, true]));
351 assert!(!is_dominating_set(4, &edges, &[false, true, false, false]));
353 assert!(!is_dominating_set(4, &edges, &[false, false, false, false]));
355 }
356
357 #[test]
358 fn test_constraints() {
359 let problem = DominatingSet::<i32>::new(3, vec![(0, 1), (1, 2)]);
360 let constraints = problem.constraints();
361 assert_eq!(constraints.len(), 3); }
363
364 #[test]
365 fn test_energy_mode() {
366 let problem = DominatingSet::<i32>::new(2, vec![(0, 1)]);
367 assert!(problem.energy_mode().is_minimization());
368 }
369
370 #[test]
371 fn test_isolated_vertex() {
372 let problem = DominatingSet::<i32>::new(3, vec![(0, 1)]);
374 let solver = BruteForce::new();
375
376 let solutions = solver.find_best(&problem);
377 for sol in &solutions {
379 assert_eq!(sol[2], 1);
380 assert!(problem.solution_size(sol).is_valid);
381 }
382 }
383
384 #[test]
385 fn test_is_satisfied() {
386 let problem = DominatingSet::<i32>::new(4, vec![(0, 1), (0, 2), (0, 3)]);
387
388 assert!(problem.is_satisfied(&[1, 0, 0, 0])); assert!(problem.is_satisfied(&[0, 1, 1, 1])); assert!(!problem.is_satisfied(&[0, 1, 0, 0])); }
392
393 #[test]
394 fn test_objectives() {
395 let problem = DominatingSet::with_weights(3, vec![(0, 1)], vec![5, 10, 15]);
396 let objectives = problem.objectives();
397 assert_eq!(objectives.len(), 3);
398 }
399
400 #[test]
401 fn test_set_weights() {
402 let mut problem = DominatingSet::<i32>::new(3, vec![(0, 1)]);
403 assert!(!problem.is_weighted()); problem.set_weights(vec![1, 2, 3]);
405 assert!(problem.is_weighted());
406 assert_eq!(problem.weights(), vec![1, 2, 3]);
407 }
408
409 #[test]
410 fn test_is_weighted_empty() {
411 let problem = DominatingSet::<i32>::with_weights(0, vec![], vec![]);
412 assert!(!problem.is_weighted());
413 }
414
415 #[test]
416 fn test_is_dominating_set_wrong_len() {
417 assert!(!is_dominating_set(3, &[(0, 1)], &[true, false]));
418 }
419
420 #[test]
421 fn test_problem_size() {
422 let problem = DominatingSet::<i32>::new(5, vec![(0, 1), (1, 2), (2, 3)]);
423 let size = problem.problem_size();
424 assert_eq!(size.get("num_vertices"), Some(5));
425 assert_eq!(size.get("num_edges"), Some(3));
426 }
427}