problemreductions/models/graph/
minimum_dominating_set.rs1use 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#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct MinimumDominatingSet<G, W> {
52 graph: G,
54 weights: Vec<W>,
56}
57
58impl<G: Graph, W: Clone + Default> MinimumDominatingSet<G, W> {
59 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 pub fn graph(&self) -> &G {
71 &self.graph
72 }
73
74 pub fn neighbors(&self, v: usize) -> Vec<usize> {
76 self.graph.neighbors(v)
77 }
78
79 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 pub fn weights(&self) -> &[W] {
88 &self.weights
89 }
90
91 pub fn is_valid_solution(&self, config: &[usize]) -> bool {
93 self.is_dominating(config)
94 }
95
96 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 dominated[v] = true;
105 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 pub fn num_vertices(&self) -> usize {
121 self.graph().num_vertices()
122 }
123
124 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#[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 for v in 0..graph.num_vertices() {
190 if selected[v] {
191 continue; }
193 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;