1use crate::models::decision::Decision;
7use crate::registry::{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", "i32", &["i32", "One"]),
23 ],
24 module_path: module_path!(),
25 description: "Find minimum weight dominating set in a graph",
26 fields: &[
27 FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E)" },
28 FieldInfo { name: "weights", type_name: "Vec<W>", description: "Vertex weights w: V -> R" },
29 ],
30 }
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct MinimumDominatingSet<G, W> {
59 graph: G,
61 weights: Vec<W>,
63}
64
65impl<G: Graph, W: Clone + Default> MinimumDominatingSet<G, W> {
66 pub fn new(graph: G, weights: Vec<W>) -> Self {
68 assert_eq!(
69 weights.len(),
70 graph.num_vertices(),
71 "weights length must match graph num_vertices"
72 );
73 Self { graph, weights }
74 }
75
76 pub fn graph(&self) -> &G {
78 &self.graph
79 }
80
81 pub fn neighbors(&self, v: usize) -> Vec<usize> {
83 self.graph.neighbors(v)
84 }
85
86 pub fn closed_neighborhood(&self, v: usize) -> HashSet<usize> {
88 let mut neighborhood: HashSet<usize> = self.neighbors(v).into_iter().collect();
89 neighborhood.insert(v);
90 neighborhood
91 }
92
93 pub fn weights(&self) -> &[W] {
95 &self.weights
96 }
97
98 pub fn is_valid_solution(&self, config: &[usize]) -> bool {
100 self.is_dominating(config)
101 }
102
103 fn is_dominating(&self, config: &[usize]) -> bool {
105 let n = self.graph.num_vertices();
106 let mut dominated = vec![false; n];
107
108 for (v, &selected) in config.iter().enumerate() {
109 if selected == 1 {
110 dominated[v] = true;
112 for neighbor in self.neighbors(v) {
114 if neighbor < n {
115 dominated[neighbor] = true;
116 }
117 }
118 }
119 }
120
121 dominated.iter().all(|&d| d)
122 }
123}
124
125impl<G: Graph, W: WeightElement> MinimumDominatingSet<G, W> {
126 pub fn num_vertices(&self) -> usize {
128 self.graph().num_vertices()
129 }
130
131 pub fn num_edges(&self) -> usize {
133 self.graph().num_edges()
134 }
135}
136
137impl<G, W> Problem for MinimumDominatingSet<G, W>
138where
139 G: Graph + crate::variant::VariantParam,
140 W: WeightElement + crate::variant::VariantParam,
141{
142 const NAME: &'static str = "MinimumDominatingSet";
143 type Value = Min<W::Sum>;
144
145 fn variant() -> Vec<(&'static str, &'static str)> {
146 crate::variant_params![G, W]
147 }
148
149 fn dims(&self) -> Vec<usize> {
150 vec![2; self.graph.num_vertices()]
151 }
152
153 fn evaluate(&self, config: &[usize]) -> Min<W::Sum> {
154 if !self.is_dominating(config) {
155 return Min(None);
156 }
157 let mut total = W::Sum::zero();
158 for (i, &selected) in config.iter().enumerate() {
159 if selected == 1 {
160 total += self.weights[i].to_sum();
161 }
162 }
163 Min(Some(total))
164 }
165}
166
167crate::declare_variants! {
168 default MinimumDominatingSet<SimpleGraph, i32> => "1.4969^num_vertices",
169 MinimumDominatingSet<SimpleGraph, One> => "1.4969^num_vertices",
170}
171
172impl<G, W> crate::models::decision::DecisionProblemMeta for MinimumDominatingSet<G, W>
173where
174 G: Graph + crate::variant::VariantParam,
175 W: WeightElement + crate::variant::VariantParam,
176 W::Sum: std::fmt::Debug + serde::Serialize + serde::de::DeserializeOwned,
177{
178 const DECISION_NAME: &'static str = "DecisionMinimumDominatingSet";
179}
180
181impl Decision<MinimumDominatingSet<SimpleGraph, i32>> {
182 pub fn num_vertices(&self) -> usize {
184 self.inner().num_vertices()
185 }
186
187 pub fn num_edges(&self) -> usize {
189 self.inner().num_edges()
190 }
191
192 pub fn k(&self) -> usize {
194 (*self.bound()).try_into().unwrap_or(0)
195 }
196}
197
198impl Decision<MinimumDominatingSet<SimpleGraph, One>> {
199 pub fn num_vertices(&self) -> usize {
201 self.inner().num_vertices()
202 }
203
204 pub fn num_edges(&self) -> usize {
206 self.inner().num_edges()
207 }
208
209 pub fn k(&self) -> usize {
211 (*self.bound()).try_into().unwrap_or(0)
212 }
213}
214
215crate::register_decision_variant!(
216 MinimumDominatingSet<SimpleGraph, i32>,
217 "DecisionMinimumDominatingSet",
218 "1.4969^num_vertices",
219 &[],
220 "Decision version: does a dominating set of cost <= bound exist?",
221 dims: [
222 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
223 VariantDimension::new("weight", "i32", &["i32", "One"]),
224 ],
225 fields: [
226 FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E)" },
227 FieldInfo { name: "weights", type_name: "Vec<W>", description: "Vertex weights w: V -> R" },
228 FieldInfo { name: "bound", type_name: "i32", description: "Decision bound (maximum allowed dominating-set cost)" },
229 ],
230 size_getters: [("num_vertices", num_vertices), ("num_edges", num_edges)]
231);
232
233impl crate::traits::DeclaredVariant for Decision<MinimumDominatingSet<SimpleGraph, One>> {}
234
235inventory::submit! {
236 crate::registry::VariantEntry {
237 name: <Decision<MinimumDominatingSet<SimpleGraph, One>> as Problem>::NAME,
238 variant_fn: <Decision<MinimumDominatingSet<SimpleGraph, One>> as Problem>::variant,
239 complexity: "1.4969^num_vertices",
240 complexity_eval_fn: |any| {
241 let problem = any
242 .downcast_ref::<Decision<MinimumDominatingSet<SimpleGraph, One>>>()
243 .expect("DecisionMinimumDominatingSet complexity source type mismatch");
244 1.4969_f64.powf(problem.num_vertices() as f64)
245 },
246 is_default: false,
247 aliases: &[],
248 factory: |data| {
249 serde_json::from_value::<Decision<MinimumDominatingSet<SimpleGraph, One>>>(data)
250 .map(|problem| Box::new(problem) as Box<dyn crate::registry::DynProblem>)
251 },
252 serialize_fn: |any| {
253 any.downcast_ref::<Decision<MinimumDominatingSet<SimpleGraph, One>>>()
254 .and_then(|problem| serde_json::to_value(problem).ok())
255 },
256 solve_value_fn: |any| {
257 let problem = any
258 .downcast_ref::<Decision<MinimumDominatingSet<SimpleGraph, One>>>()
259 .expect("DecisionMinimumDominatingSet value solve source type mismatch");
260 let (value, _) = crate::solvers::BruteForce::new().solve_with_witnesses(problem);
261 crate::registry::format_metric(&value)
262 },
263 solve_witness_fn: |any| {
264 let problem = any
265 .downcast_ref::<Decision<MinimumDominatingSet<SimpleGraph, One>>>()
266 .expect("DecisionMinimumDominatingSet witness solve source type mismatch");
267 let (value, witnesses) = crate::solvers::BruteForce::new().solve_with_witnesses(problem);
268 witnesses
269 .into_iter()
270 .next()
271 .map(|config| (config, crate::registry::format_metric(&value)))
272 },
273 }
274}
275
276inventory::submit! {
278 crate::rules::ReductionEntry {
279 source_name: "DecisionMinimumDominatingSet",
280 target_name: "MinimumDominatingSet",
281 source_variant_fn: <Decision<MinimumDominatingSet<SimpleGraph, One>> as Problem>::variant,
282 target_variant_fn: <MinimumDominatingSet<SimpleGraph, One> as Problem>::variant,
283 overhead_fn: || crate::rules::ReductionOverhead::identity(&["num_vertices", "num_edges"]),
284 module_path: module_path!(),
285 reduce_fn: Some(|any| {
286 let source = any
287 .downcast_ref::<Decision<MinimumDominatingSet<SimpleGraph, One>>>()
288 .expect("DecisionMinimumDominatingSet witness reduction source type mismatch");
289 Box::new(
290 <Decision<MinimumDominatingSet<SimpleGraph, One>> as crate::rules::ReduceTo<
291 MinimumDominatingSet<SimpleGraph, One>,
292 >>::reduce_to(source),
293 )
294 }),
295 reduce_aggregate_fn: Some(|any| {
296 let source = any
297 .downcast_ref::<Decision<MinimumDominatingSet<SimpleGraph, One>>>()
298 .expect("DecisionMinimumDominatingSet aggregate reduction source type mismatch");
299 Box::new(
300 <Decision<MinimumDominatingSet<SimpleGraph, One>> as crate::rules::ReduceToAggregate<
301 MinimumDominatingSet<SimpleGraph, One>,
302 >>::reduce_to_aggregate(source),
303 )
304 }),
305 capabilities: crate::rules::EdgeCapabilities::both(),
306 overhead_eval_fn: |any| {
307 let source = any
308 .downcast_ref::<Decision<MinimumDominatingSet<SimpleGraph, One>>>()
309 .expect("DecisionMinimumDominatingSet overhead source type mismatch");
310 crate::types::ProblemSize::new(vec![
311 ("num_vertices", source.num_vertices()),
312 ("num_edges", source.num_edges()),
313 ])
314 },
315 source_size_fn: |any| {
316 let source = any
317 .downcast_ref::<Decision<MinimumDominatingSet<SimpleGraph, One>>>()
318 .expect("DecisionMinimumDominatingSet size source type mismatch");
319 crate::types::ProblemSize::new(vec![
320 ("num_vertices", source.num_vertices()),
321 ("num_edges", source.num_edges()),
322 ("k", source.k()),
323 ])
324 },
325 }
326}
327
328inventory::submit! {
330 crate::rules::ReductionEntry {
331 source_name: "MinimumDominatingSet",
332 target_name: "DecisionMinimumDominatingSet",
333 source_variant_fn: <MinimumDominatingSet<SimpleGraph, One> as Problem>::variant,
334 target_variant_fn: <Decision<MinimumDominatingSet<SimpleGraph, One>> as Problem>::variant,
335 overhead_fn: || crate::rules::ReductionOverhead::identity(&["num_vertices", "num_edges"]),
336 module_path: module_path!(),
337 reduce_fn: None,
338 reduce_aggregate_fn: None,
339 capabilities: crate::rules::EdgeCapabilities::turing(),
340 overhead_eval_fn: |any| {
341 let source = any
342 .downcast_ref::<MinimumDominatingSet<SimpleGraph, One>>()
343 .expect("DecisionMinimumDominatingSet turing overhead source type mismatch");
344 crate::types::ProblemSize::new(vec![
345 ("num_vertices", source.num_vertices()),
346 ("num_edges", source.num_edges()),
347 ])
348 },
349 source_size_fn: |any| {
350 let source = any
351 .downcast_ref::<MinimumDominatingSet<SimpleGraph, One>>()
352 .expect("DecisionMinimumDominatingSet turing size source type mismatch");
353 crate::types::ProblemSize::new(vec![
354 ("num_vertices", source.num_vertices()),
355 ("num_edges", source.num_edges()),
356 ])
357 },
358 }
359}
360
361#[cfg(feature = "example-db")]
362pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
363 vec![crate::example_db::specs::ModelExampleSpec {
364 id: "minimum_dominating_set_simplegraph_i32",
365 instance: Box::new(MinimumDominatingSet::new(
366 SimpleGraph::new(5, vec![(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (3, 4)]),
367 vec![1i32; 5],
368 )),
369 optimal_config: vec![0, 0, 1, 1, 0],
370 optimal_value: serde_json::json!(2),
371 }]
372}
373
374#[cfg(feature = "example-db")]
375pub(crate) fn decision_canonical_model_example_specs(
376) -> Vec<crate::example_db::specs::ModelExampleSpec> {
377 vec![
378 crate::example_db::specs::ModelExampleSpec {
379 id: "decision_minimum_dominating_set_simplegraph_i32",
380 instance: Box::new(Decision::new(
381 MinimumDominatingSet::new(
382 SimpleGraph::new(5, vec![(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (3, 4)]),
383 vec![1i32; 5],
384 ),
385 2,
386 )),
387 optimal_config: vec![0, 0, 1, 1, 0],
388 optimal_value: serde_json::json!(true),
389 },
390 crate::example_db::specs::ModelExampleSpec {
391 id: "decision_minimum_dominating_set_simplegraph_one",
392 instance: Box::new(Decision::new(
393 MinimumDominatingSet::new(
394 SimpleGraph::new(
395 6,
396 vec![(0, 1), (0, 2), (1, 3), (2, 3), (3, 4), (3, 5), (4, 5)],
397 ),
398 vec![One; 6],
399 ),
400 2,
401 )),
402 optimal_config: vec![1, 0, 0, 1, 0, 0],
403 optimal_value: serde_json::json!(true),
404 },
405 ]
406}
407
408#[cfg(feature = "example-db")]
409pub(crate) fn decision_canonical_rule_example_specs(
410) -> Vec<crate::example_db::specs::RuleExampleSpec> {
411 vec![
412 crate::example_db::specs::RuleExampleSpec {
413 id: "decision_minimum_dominating_set_to_minimum_dominating_set",
414 build: || {
415 use crate::example_db::specs::assemble_rule_example;
416 use crate::export::SolutionPair;
417 use crate::rules::{AggregateReductionResult, ReduceToAggregate};
418
419 let source = crate::models::decision::Decision::new(
420 MinimumDominatingSet::new(
421 SimpleGraph::new(5, vec![(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (3, 4)]),
422 vec![1i32; 5],
423 ),
424 2,
425 );
426 let result = source.reduce_to_aggregate();
427 let target = result.target_problem();
428 let config = vec![0, 0, 1, 1, 0];
429 assemble_rule_example(
430 &source,
431 target,
432 vec![SolutionPair {
433 source_config: config.clone(),
434 target_config: config,
435 }],
436 )
437 },
438 },
439 crate::example_db::specs::RuleExampleSpec {
441 id: "decision_minimum_dominating_set_one_to_minimum_dominating_set_one",
442 build: || {
443 use crate::example_db::specs::assemble_rule_example;
444 use crate::export::SolutionPair;
445 use crate::rules::{AggregateReductionResult, ReduceToAggregate};
446
447 let source = crate::models::decision::Decision::new(
448 MinimumDominatingSet::new(
449 SimpleGraph::new(5, vec![(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (3, 4)]),
450 vec![One; 5],
451 ),
452 2,
453 );
454 let result = source.reduce_to_aggregate();
455 let target = result.target_problem();
456 let config = vec![0, 0, 1, 1, 0];
457 assemble_rule_example(
458 &source,
459 target,
460 vec![SolutionPair {
461 source_config: config.clone(),
462 target_config: config,
463 }],
464 )
465 },
466 },
467 ]
468}
469
470#[cfg(test)]
475pub(crate) fn is_dominating_set<G: Graph>(graph: &G, selected: &[bool]) -> bool {
476 assert_eq!(
477 selected.len(),
478 graph.num_vertices(),
479 "selected length must match num_vertices"
480 );
481
482 for v in 0..graph.num_vertices() {
484 if selected[v] {
485 continue; }
487 if !graph.neighbors(v).iter().any(|&u| selected[u]) {
489 return false;
490 }
491 }
492
493 true
494}
495
496#[cfg(test)]
497#[path = "../../unit_tests/models/graph/minimum_dominating_set.rs"]
498mod tests;