problemreductions/models/graph/
minimum_dominating_set.rs

1//! Dominating Set problem implementation.
2//!
3//! The Dominating Set problem asks for a minimum weight subset of vertices
4//! such that every vertex is either in the set or adjacent to a vertex in the set.
5
6use crate::registry::{FieldInfo, ProblemSchemaEntry};
7use crate::topology::{Graph, SimpleGraph};
8use crate::traits::{OptimizationProblem, Problem};
9use crate::types::{Direction, SolutionSize, WeightElement};
10use num_traits::Zero;
11use serde::{Deserialize, Serialize};
12use std::collections::HashSet;
13
14inventory::submit! {
15    ProblemSchemaEntry {
16        name: "MinimumDominatingSet",
17        module_path: module_path!(),
18        description: "Find minimum weight dominating set in a graph",
19        fields: &[
20            FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E)" },
21            FieldInfo { name: "weights", type_name: "Vec<W>", description: "Vertex weights w: V -> R" },
22        ],
23    }
24}
25
26/// The Dominating Set problem.
27///
28/// Given a graph G = (V, E) and weights w_v for each vertex,
29/// find a subset D ⊆ V such that:
30/// - Every vertex is either in D or adjacent to a vertex in D (domination)
31/// - The total weight Σ_{v ∈ D} w_v is minimized
32///
33/// # Example
34///
35/// ```
36/// use problemreductions::models::graph::MinimumDominatingSet;
37/// use problemreductions::topology::SimpleGraph;
38/// use problemreductions::{Problem, Solver, BruteForce};
39///
40/// // Star graph: center dominates all
41/// let graph = SimpleGraph::new(4, vec![(0, 1), (0, 2), (0, 3)]);
42/// let problem = MinimumDominatingSet::new(graph, vec![1; 4]);
43///
44/// let solver = BruteForce::new();
45/// let solutions = solver.find_all_best(&problem);
46///
47/// // Minimum dominating set is just the center vertex
48/// assert!(solutions.contains(&vec![1, 0, 0, 0]));
49/// ```
50#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct MinimumDominatingSet<G, W> {
52    /// The underlying graph.
53    graph: G,
54    /// Weights for each vertex.
55    weights: Vec<W>,
56}
57
58impl<G: Graph, W: Clone + Default> MinimumDominatingSet<G, W> {
59    /// Create a Dominating Set problem from a graph with given weights.
60    pub fn new(graph: G, weights: Vec<W>) -> Self {
61        assert_eq!(
62            weights.len(),
63            graph.num_vertices(),
64            "weights length must match graph num_vertices"
65        );
66        Self { graph, weights }
67    }
68
69    /// Get a reference to the underlying graph.
70    pub fn graph(&self) -> &G {
71        &self.graph
72    }
73
74    /// Get neighbors of a vertex.
75    pub fn neighbors(&self, v: usize) -> Vec<usize> {
76        self.graph.neighbors(v)
77    }
78
79    /// Get the closed neighborhood `N[v] = {v} ∪ N(v)`.
80    pub fn closed_neighborhood(&self, v: usize) -> HashSet<usize> {
81        let mut neighborhood: HashSet<usize> = self.neighbors(v).into_iter().collect();
82        neighborhood.insert(v);
83        neighborhood
84    }
85
86    /// Get a reference to the weights slice.
87    pub fn weights(&self) -> &[W] {
88        &self.weights
89    }
90
91    /// Check if a configuration is a valid dominating set.
92    pub fn is_valid_solution(&self, config: &[usize]) -> bool {
93        self.is_dominating(config)
94    }
95
96    /// Check if a set of vertices is a dominating set.
97    fn is_dominating(&self, config: &[usize]) -> bool {
98        let n = self.graph.num_vertices();
99        let mut dominated = vec![false; n];
100
101        for (v, &selected) in config.iter().enumerate() {
102            if selected == 1 {
103                // v dominates itself
104                dominated[v] = true;
105                // v dominates all its neighbors
106                for neighbor in self.neighbors(v) {
107                    if neighbor < n {
108                        dominated[neighbor] = true;
109                    }
110                }
111            }
112        }
113
114        dominated.iter().all(|&d| d)
115    }
116}
117
118impl<G: Graph, W: WeightElement> MinimumDominatingSet<G, W> {
119    /// Get the number of vertices in the underlying graph.
120    pub fn num_vertices(&self) -> usize {
121        self.graph().num_vertices()
122    }
123
124    /// Get the number of edges in the underlying graph.
125    pub fn num_edges(&self) -> usize {
126        self.graph().num_edges()
127    }
128}
129
130impl<G, W> Problem for MinimumDominatingSet<G, W>
131where
132    G: Graph + crate::variant::VariantParam,
133    W: WeightElement + crate::variant::VariantParam,
134{
135    const NAME: &'static str = "MinimumDominatingSet";
136    type Metric = SolutionSize<W::Sum>;
137
138    fn variant() -> Vec<(&'static str, &'static str)> {
139        crate::variant_params![G, W]
140    }
141
142    fn dims(&self) -> Vec<usize> {
143        vec![2; self.graph.num_vertices()]
144    }
145
146    fn evaluate(&self, config: &[usize]) -> SolutionSize<W::Sum> {
147        if !self.is_dominating(config) {
148            return SolutionSize::Invalid;
149        }
150        let mut total = W::Sum::zero();
151        for (i, &selected) in config.iter().enumerate() {
152            if selected == 1 {
153                total += self.weights[i].to_sum();
154            }
155        }
156        SolutionSize::Valid(total)
157    }
158}
159
160impl<G, W> OptimizationProblem for MinimumDominatingSet<G, W>
161where
162    G: Graph + crate::variant::VariantParam,
163    W: WeightElement + crate::variant::VariantParam,
164{
165    type Value = W::Sum;
166
167    fn direction(&self) -> Direction {
168        Direction::Minimize
169    }
170}
171
172crate::declare_variants! {
173    MinimumDominatingSet<SimpleGraph, i32> => "1.4969^num_vertices",
174}
175
176/// Check if a set of vertices is a dominating set.
177///
178/// # Panics
179/// Panics if `selected.len() != graph.num_vertices()`.
180#[cfg(test)]
181pub(crate) fn is_dominating_set<G: Graph>(graph: &G, selected: &[bool]) -> bool {
182    assert_eq!(
183        selected.len(),
184        graph.num_vertices(),
185        "selected length must match num_vertices"
186    );
187
188    // Check each vertex is dominated
189    for v in 0..graph.num_vertices() {
190        if selected[v] {
191            continue; // v dominates itself
192        }
193        // Check if any neighbor of v is selected
194        if !graph.neighbors(v).iter().any(|&u| selected[u]) {
195            return false;
196        }
197    }
198
199    true
200}
201
202#[cfg(test)]
203#[path = "../../unit_tests/models/graph/minimum_dominating_set.rs"]
204mod tests;