1use crate::models::decision::Decision;
7use crate::registry::{CreateSpec, FieldInfo, ProblemSchemaEntry, VariantDimension};
8use crate::topology::{Graph, SimpleGraph};
9use crate::traits::Problem;
10use crate::types::{Min, One, WeightElement};
11use num_traits::Zero;
12use serde::{Deserialize, Serialize};
13use std::collections::HashSet;
14
15inventory::submit! {
16 ProblemSchemaEntry {
17 name: "MinimumDominatingSet",
18 display_name: "Minimum Dominating Set",
19 aliases: &[],
20 dimensions: &[
21 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
22 VariantDimension::new("weight", "i64", &["i64", "One"]),
23 ],
24 category: crate::registry::ProblemCategory::Graph,
25 module_path: module_path!(),
26 description: "Find minimum weight dominating set in a graph",
27 fields: MinimumDominatingSetCreateSpec::<i64>::FIELDS,
28 }
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
56#[serde(try_from = "MinimumDominatingSetData<G, W>")]
57#[serde(bound(deserialize = "G: Graph + Deserialize<'de>, W: Clone + Default + Deserialize<'de>"))]
58pub struct MinimumDominatingSet<G, W> {
59 graph: G,
61 weights: Vec<W>,
63}
64
65#[derive(Deserialize)]
66struct MinimumDominatingSetData<G, W> {
67 graph: G,
68 weights: Vec<W>,
69}
70
71impl<G, W> TryFrom<MinimumDominatingSetData<G, W>> for MinimumDominatingSet<G, W>
72where
73 G: Graph,
74 W: Clone + Default,
75{
76 type Error = crate::registry::ConstructionError;
77
78 fn try_from(data: MinimumDominatingSetData<G, W>) -> Result<Self, Self::Error> {
79 Self::try_new(data.graph, data.weights)
80 }
81}
82
83#[derive(Debug, Deserialize, crate::CreateSpec)]
84struct MinimumDominatingSetCreateSpec<W> {
85 graph: SimpleGraph,
87 weights: Vec<W>,
89}
90
91impl<W: Clone + Default> TryFrom<MinimumDominatingSetCreateSpec<W>>
92 for MinimumDominatingSet<SimpleGraph, W>
93{
94 type Error = crate::registry::ConstructionError;
95 fn try_from(spec: MinimumDominatingSetCreateSpec<W>) -> Result<Self, Self::Error> {
96 Self::try_new(spec.graph, spec.weights)
97 }
98}
99
100impl<G: Graph, W: Clone + Default> MinimumDominatingSet<G, W> {
101 pub fn new(graph: G, weights: Vec<W>) -> Self {
103 Self::try_new(graph, weights).unwrap_or_else(|error| panic!("{error}"))
104 }
105
106 fn try_new(graph: G, weights: Vec<W>) -> Result<Self, crate::registry::ConstructionError> {
107 if weights.len() != graph.num_vertices() {
108 return Err(crate::registry::ConstructionError::length_mismatch(
109 "weights",
110 weights.len(),
111 graph.num_vertices(),
112 ));
113 }
114 Ok(Self { graph, weights })
115 }
116
117 pub fn graph(&self) -> &G {
119 &self.graph
120 }
121
122 pub fn neighbors(&self, v: usize) -> Vec<usize> {
124 self.graph.neighbors(v)
125 }
126
127 pub fn closed_neighborhood(&self, v: usize) -> HashSet<usize> {
129 let mut neighborhood: HashSet<usize> = self.neighbors(v).into_iter().collect();
130 neighborhood.insert(v);
131 neighborhood
132 }
133
134 pub fn weights(&self) -> &[W] {
136 &self.weights
137 }
138
139 pub fn is_valid_solution(&self, config: &[bool]) -> bool {
141 self.is_dominating(config)
142 }
143
144 fn is_dominating(&self, config: &[bool]) -> bool {
146 let n = self.graph.num_vertices();
147 let mut dominated = vec![false; n];
148
149 for (v, &selected) in config.iter().enumerate() {
150 if selected {
151 dominated[v] = true;
153 for neighbor in self.neighbors(v) {
155 if neighbor < n {
156 dominated[neighbor] = true;
157 }
158 }
159 }
160 }
161
162 dominated.iter().all(|&d| d)
163 }
164}
165
166impl<G: Graph, W: WeightElement> MinimumDominatingSet<G, W> {
167 pub fn num_vertices(&self) -> usize {
169 self.graph().num_vertices()
170 }
171
172 pub fn num_edges(&self) -> usize {
174 self.graph().num_edges()
175 }
176}
177
178impl<G, W> Problem for MinimumDominatingSet<G, W>
179where
180 G: Graph + crate::variant::VariantParam,
181 W: WeightElement + crate::variant::VariantParam,
182{
183 const NAME: &'static str = "MinimumDominatingSet";
184 type Solution = Vec<bool>;
185 type Value = Min<W::Sum>;
186
187 crate::problem_parameters![("num_edges", num_edges), ("num_vertices", num_vertices),];
188
189 fn variant() -> Vec<(&'static str, &'static str)> {
190 crate::variant_params![G, W]
191 }
192
193 fn evaluate(
194 &self,
195 config: &Self::Solution,
196 ) -> Result<Min<W::Sum>, crate::traits::EvaluationError> {
197 if config.len() != self.graph.num_vertices() {
198 return Err(crate::traits::EvaluationError::InvalidConfiguration(
199 "vertex-selection length does not match the graph".into(),
200 ));
201 }
202 Ok({
203 if !self.is_dominating(config) {
204 return Ok(Min(None));
205 }
206 let mut total = W::Sum::zero();
207 for (i, &selected) in config.iter().enumerate() {
208 if selected {
209 total = W::checked_add_to_sum(
210 total,
211 self.weights[i].to_sum(),
212 "summing selected dominating-set weights",
213 )?;
214 }
215 }
216 Min(Some(total))
217 })
218 }
219}
220
221impl<G, W> crate::solvers::BruteForceProblem for MinimumDominatingSet<G, W>
222where
223 G: Graph + crate::variant::VariantParam,
224 W: WeightElement + crate::variant::VariantParam,
225{
226 fn dimensions(&self) -> Vec<usize> {
227 vec![2; self.graph.num_vertices()]
228 }
229}
230
231crate::impl_random_generate!(MinimumDominatingSet<SimpleGraph, i64>, crate::random::SimpleGraphRandomSpec, |spec| {
232 Ok(MinimumDominatingSet::new(spec.graph()?, vec![1; spec.num_vertices]))
233});
234crate::impl_random_generate!(MinimumDominatingSet<SimpleGraph, One>, crate::random::SimpleGraphRandomSpec, |spec| {
235 Ok(MinimumDominatingSet::new(spec.graph()?, vec![One; spec.num_vertices]))
236});
237
238#[derive(Debug, Deserialize, crate::CreateSpec)]
239struct MinimumDominatingSetOneCreateSpec {
240 graph: SimpleGraph,
242}
243
244impl TryFrom<MinimumDominatingSetOneCreateSpec> for MinimumDominatingSet<SimpleGraph, One> {
245 type Error = crate::registry::ConstructionError;
246 fn try_from(spec: MinimumDominatingSetOneCreateSpec) -> Result<Self, Self::Error> {
247 let weights = vec![One; spec.graph.num_vertices()];
248 Self::try_new(spec.graph, weights)
249 }
250}
251
252crate::declare_variants! {
253 default MinimumDominatingSet<SimpleGraph, i64> => "1.4969^num_vertices" create MinimumDominatingSetCreateSpec<i64> random,
254 MinimumDominatingSet<SimpleGraph, One> => "1.4969^num_vertices" create MinimumDominatingSetOneCreateSpec random,
255}
256
257crate::register_brute_force! {
258 MinimumDominatingSet<SimpleGraph, i64> decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
259 MinimumDominatingSet<SimpleGraph, One> decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
260}
261
262impl<G, W> crate::models::decision::DecisionProblemMeta for MinimumDominatingSet<G, W>
263where
264 G: Graph + crate::variant::VariantParam,
265 W: WeightElement + crate::variant::VariantParam,
266 W::Sum: std::fmt::Debug + serde::Serialize + serde::de::DeserializeOwned,
267{
268 const DECISION_NAME: &'static str = "DecisionMinimumDominatingSet";
269}
270
271impl Decision<MinimumDominatingSet<SimpleGraph, i64>> {
272 pub fn num_vertices(&self) -> usize {
274 self.inner().num_vertices()
275 }
276
277 pub fn num_edges(&self) -> usize {
279 self.inner().num_edges()
280 }
281
282 pub fn k(&self) -> usize {
284 (*self.bound()).try_into().unwrap_or(0)
285 }
286}
287
288impl Decision<MinimumDominatingSet<SimpleGraph, One>> {
289 pub fn num_vertices(&self) -> usize {
291 self.inner().num_vertices()
292 }
293
294 pub fn num_edges(&self) -> usize {
296 self.inner().num_edges()
297 }
298
299 pub fn k(&self) -> usize {
301 (*self.bound()).try_into().unwrap_or(0)
302 }
303}
304
305crate::register_decision_variant!(
306 MinimumDominatingSet<SimpleGraph, i64>,
307 "DecisionMinimumDominatingSet",
308 "1.4969^num_vertices",
309 &[],
310 "Decision version: does a dominating set of cost <= bound exist?",
311 category: crate::registry::ProblemCategory::Graph,
312 dims: [
313 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
314 VariantDimension::new("weight", "i64", &["i64", "One"]),
315 ],
316 fields: [
317 FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E)" },
318 FieldInfo { name: "weights", type_name: "Vec<W>", description: "Vertex weights w: V -> R" },
319 FieldInfo { name: "bound", type_name: "i64", description: "Decision bound (maximum allowed dominating-set cost)" },
320 ],
321 additional: [MinimumDominatingSet<SimpleGraph, One> => "1.4969^num_vertices"],
322 decode: |_, indices: Vec<usize>| crate::config::config_to_bits(&indices)
323);
324
325#[cfg(feature = "example-db")]
326pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
327 vec![crate::example_db::specs::ModelExampleSpec {
328 id: "minimum_dominating_set_simplegraph",
329 instance: Box::new(MinimumDominatingSet::new(
330 SimpleGraph::new(5, vec![(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (3, 4)]),
331 vec![1i64; 5],
332 )),
333 optimal_config: serde_json::json!(vec![false, false, true, true, false]),
334 optimal_value: serde_json::json!(2),
335 }]
336}
337
338#[cfg(feature = "example-db")]
339pub(crate) fn decision_canonical_model_example_specs(
340) -> Vec<crate::example_db::specs::ModelExampleSpec> {
341 vec![
342 crate::example_db::specs::ModelExampleSpec {
343 id: "decision_minimum_dominating_set_simplegraph",
344 instance: Box::new(Decision::new(
345 MinimumDominatingSet::new(
346 SimpleGraph::new(5, vec![(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (3, 4)]),
347 vec![1i64; 5],
348 ),
349 2,
350 )),
351 optimal_config: serde_json::json!(vec![false, false, true, true, false]),
352 optimal_value: serde_json::json!(true),
353 },
354 crate::example_db::specs::ModelExampleSpec {
355 id: "decision_minimum_dominating_set_six_vertex_graph",
356 instance: Box::new(Decision::new(
357 MinimumDominatingSet::new(
358 SimpleGraph::new(
359 6,
360 vec![(0, 1), (0, 2), (1, 3), (2, 3), (3, 4), (3, 5), (4, 5)],
361 ),
362 vec![One; 6],
363 ),
364 2,
365 )),
366 optimal_config: serde_json::json!(vec![true, false, false, true, false, false]),
367 optimal_value: serde_json::json!(true),
368 },
369 ]
370}
371
372#[cfg(feature = "example-db")]
373pub(crate) fn decision_canonical_rule_example_specs(
374) -> Vec<crate::example_db::specs::RuleExampleSpec> {
375 vec![
376 crate::example_db::specs::RuleExampleSpec {
377 id: "decision_minimum_dominating_set_to_minimum_dominating_set",
378 build: || {
379 use crate::example_db::specs::assemble_rule_example;
380 use crate::export::SolutionPair;
381 use crate::rules::{AggregateReductionResult, ReduceToAggregate};
382
383 let source = crate::models::decision::Decision::new(
384 MinimumDominatingSet::new(
385 SimpleGraph::new(5, vec![(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (3, 4)]),
386 vec![1i64; 5],
387 ),
388 2,
389 );
390 let result = source
391 .reduce_to_aggregate()
392 .expect("reduction should succeed");
393 let target = result.target_problem();
394 let config = vec![false, false, true, true, false];
395 assemble_rule_example(
396 &source,
397 target,
398 vec![SolutionPair {
399 source_config: serde_json::json!(config.clone()),
400 target_config: serde_json::json!(config),
401 }],
402 )
403 },
404 },
405 crate::example_db::specs::RuleExampleSpec {
407 id: "decision_cardinality_dominating_set_to_minimum_dominating_set",
408 build: || {
409 use crate::example_db::specs::assemble_rule_example;
410 use crate::export::SolutionPair;
411 use crate::rules::{AggregateReductionResult, ReduceToAggregate};
412
413 let source = crate::models::decision::Decision::new(
414 MinimumDominatingSet::new(
415 SimpleGraph::new(5, vec![(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (3, 4)]),
416 vec![One; 5],
417 ),
418 2,
419 );
420 let result = source
421 .reduce_to_aggregate()
422 .expect("reduction should succeed");
423 let target = result.target_problem();
424 let config = vec![false, false, true, true, false];
425 assemble_rule_example(
426 &source,
427 target,
428 vec![SolutionPair {
429 source_config: serde_json::json!(config.clone()),
430 target_config: serde_json::json!(config),
431 }],
432 )
433 },
434 },
435 ]
436}
437
438#[cfg(test)]
443pub(crate) fn is_dominating_set<G: Graph>(graph: &G, selected: &[bool]) -> bool {
444 assert_eq!(
445 selected.len(),
446 graph.num_vertices(),
447 "selected length must match num_vertices"
448 );
449
450 for v in 0..graph.num_vertices() {
452 if selected[v] {
453 continue; }
455 if !graph.neighbors(v).iter().any(|&u| selected[u]) {
457 return false;
458 }
459 }
460
461 true
462}
463
464#[cfg(test)]
465#[path = "../../unit_tests/models/graph/minimum_dominating_set.rs"]
466mod tests;