problemreductions/rules/minimumdominatingset_ilp.rs
1//! Reduction from MinimumDominatingSet to ILP (Integer Linear Programming).
2//!
3//! The Dominating Set problem can be formulated as a binary ILP:
4//! - Variables: One binary variable per vertex (0 = not selected, 1 = selected)
5//! - Constraints: For each vertex v: x_v + sum_{u in N(v)} x_u >= 1
6//! (v or at least one of its neighbors must be selected)
7//! - Objective: Minimize the sum of weights of selected vertices
8
9use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
10use crate::models::graph::MinimumDominatingSet;
11use crate::reduction;
12use crate::rules::traits::{ReduceTo, ReductionResult};
13use crate::topology::{Graph, SimpleGraph};
14
15/// Result of reducing MinimumDominatingSet to ILP.
16///
17/// This reduction creates a binary ILP where:
18/// - Each vertex corresponds to a binary variable
19/// - For each vertex v, the constraint x_v + sum_{u in N(v)} x_u >= 1 ensures
20/// that v is dominated (either v itself or one of its neighbors is selected)
21/// - The objective minimizes the total weight of selected vertices
22#[derive(Debug, Clone)]
23pub struct ReductionDSToILP {
24 target: ILP<bool>,
25}
26
27impl ReductionResult for ReductionDSToILP {
28 type Source = MinimumDominatingSet<SimpleGraph, i32>;
29 type Target = ILP<bool>;
30
31 fn target_problem(&self) -> &ILP<bool> {
32 &self.target
33 }
34
35 /// Extract solution from ILP back to MinimumDominatingSet.
36 ///
37 /// Since the mapping is 1:1 (each vertex maps to one binary variable),
38 /// the solution extraction is simply copying the configuration.
39 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
40 target_solution.to_vec()
41 }
42}
43
44#[reduction(
45 overhead = {
46 num_vars = "num_vertices",
47 num_constraints = "num_vertices",
48 }
49)]
50impl ReduceTo<ILP<bool>> for MinimumDominatingSet<SimpleGraph, i32> {
51 type Result = ReductionDSToILP;
52
53 fn reduce_to(&self) -> Self::Result {
54 let num_vars = self.graph().num_vertices();
55
56 // Constraints: For each vertex v, x_v + sum_{u in N(v)} x_u >= 1
57 // This ensures that v is dominated (either selected or has a selected neighbor)
58 let constraints: Vec<LinearConstraint> = (0..num_vars)
59 .map(|v| {
60 // Build terms: x_v with coefficient 1, plus each neighbor with coefficient 1
61 let mut terms: Vec<(usize, f64)> = vec![(v, 1.0)];
62 for neighbor in self.neighbors(v) {
63 terms.push((neighbor, 1.0));
64 }
65 LinearConstraint::ge(terms, 1.0)
66 })
67 .collect();
68
69 // Objective: minimize sum of w_i * x_i (weighted sum of selected vertices)
70 let objective: Vec<(usize, f64)> = self
71 .weights()
72 .iter()
73 .enumerate()
74 .map(|(i, &w)| (i, w as f64))
75 .collect();
76
77 let target = ILP::new(num_vars, constraints, objective, ObjectiveSense::Minimize);
78
79 ReductionDSToILP { target }
80 }
81}
82
83#[cfg(test)]
84#[path = "../unit_tests/rules/minimumdominatingset_ilp.rs"]
85mod tests;