Skip to main content

problemreductions/models/graph/
maximum_domatic_number.rs

1//! Maximum Domatic Number problem implementation.
2//!
3//! The Maximum Domatic Number problem asks for the maximum number k such that the
4//! vertex set V of a graph G=(V,E) can be partitioned into k disjoint dominating sets.
5
6use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
7use crate::topology::{Graph, SimpleGraph};
8use crate::traits::Problem;
9use crate::types::Max;
10use serde::{Deserialize, Serialize};
11
12inventory::submit! {
13    ProblemSchemaEntry {
14        name: "MaximumDomaticNumber",
15        display_name: "Maximum Domatic Number",
16        aliases: &[],
17        dimensions: &[
18            VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
19        ],
20        module_path: module_path!(),
21        description: "Find maximum number of disjoint dominating sets partitioning V",
22        fields: &[
23            FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E)" },
24        ],
25    }
26}
27
28/// The Maximum Domatic Number problem.
29///
30/// Given a graph G = (V, E), find the maximum k such that V can be partitioned
31/// into k disjoint dominating sets. A dominating set D ⊆ V is a set such that
32/// every vertex is either in D or adjacent to a vertex in D.
33///
34/// The configuration assigns each vertex to a set index (0..n-1). The value is
35/// `Max(Some(k))` where k is the number of non-empty sets if all non-empty sets
36/// are dominating, or `Max(None)` if any non-empty set fails domination.
37///
38/// # Example
39///
40/// ```
41/// use problemreductions::models::graph::MaximumDomaticNumber;
42/// use problemreductions::topology::SimpleGraph;
43/// use problemreductions::{Problem, Solver, BruteForce};
44///
45/// // Path graph P3: 0-1-2
46/// let graph = SimpleGraph::new(3, vec![(0, 1), (1, 2)]);
47/// let problem = MaximumDomaticNumber::new(graph);
48///
49/// let solver = BruteForce::new();
50/// let witness = solver.find_witness(&problem).unwrap();
51/// let value = problem.evaluate(&witness);
52/// // Domatic number of P3 is 2
53/// assert_eq!(value, problemreductions::types::Max(Some(2)));
54/// ```
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct MaximumDomaticNumber<G> {
57    /// The underlying graph.
58    graph: G,
59}
60
61impl<G: Graph> MaximumDomaticNumber<G> {
62    /// Create a Maximum Domatic Number problem from a graph.
63    pub fn new(graph: G) -> Self {
64        Self { graph }
65    }
66
67    /// Get a reference to the underlying graph.
68    pub fn graph(&self) -> &G {
69        &self.graph
70    }
71
72    /// Get the number of vertices in the underlying graph.
73    pub fn num_vertices(&self) -> usize {
74        self.graph.num_vertices()
75    }
76
77    /// Get the number of edges in the underlying graph.
78    pub fn num_edges(&self) -> usize {
79        self.graph.num_edges()
80    }
81
82    /// Check whether a partition is valid (all non-empty sets are dominating).
83    ///
84    /// Returns `Some(k)` where k is the number of non-empty dominating sets,
85    /// or `None` if any non-empty set fails the domination property.
86    fn evaluate_partition(&self, config: &[usize]) -> Option<usize> {
87        let n = self.graph.num_vertices();
88
89        // Configuration must assign each vertex to exactly one set.
90        if config.len() != n {
91            return None;
92        }
93
94        // Collect which vertices belong to each set
95        let mut sets: Vec<Vec<usize>> = vec![vec![]; n];
96        for (v, &set_idx) in config.iter().enumerate() {
97            // Each set index must be within bounds of the available sets.
98            if set_idx >= n {
99                return None;
100            }
101            sets[set_idx].push(v);
102        }
103
104        // Check each non-empty set is a dominating set
105        let mut count = 0;
106        for set in &sets {
107            if set.is_empty() {
108                continue;
109            }
110            count += 1;
111
112            // Build membership lookup
113            let mut in_set = vec![false; n];
114            for &v in set {
115                in_set[v] = true;
116            }
117
118            // Every vertex must be in the set or adjacent to someone in the set
119            for v in 0..n {
120                if in_set[v] {
121                    continue;
122                }
123                if !self.graph.neighbors(v).iter().any(|&u| in_set[u]) {
124                    return None;
125                }
126            }
127        }
128
129        Some(count)
130    }
131}
132
133impl<G> Problem for MaximumDomaticNumber<G>
134where
135    G: Graph + crate::variant::VariantParam,
136{
137    const NAME: &'static str = "MaximumDomaticNumber";
138    type Value = Max<usize>;
139
140    fn variant() -> Vec<(&'static str, &'static str)> {
141        crate::variant_params![G]
142    }
143
144    fn dims(&self) -> Vec<usize> {
145        let n = self.graph.num_vertices();
146        vec![n; n]
147    }
148
149    fn evaluate(&self, config: &[usize]) -> Max<usize> {
150        match self.evaluate_partition(config) {
151            Some(k) => Max(Some(k)),
152            None => Max(None),
153        }
154    }
155}
156
157crate::declare_variants! {
158    default MaximumDomaticNumber<SimpleGraph> => "2.695^num_vertices",
159}
160
161#[cfg(feature = "example-db")]
162pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
163    vec![crate::example_db::specs::ModelExampleSpec {
164        id: "maximum_domatic_number_simplegraph",
165        instance: Box::new(MaximumDomaticNumber::new(SimpleGraph::new(
166            6,
167            vec![
168                (0, 1),
169                (0, 2),
170                (0, 3),
171                (1, 4),
172                (2, 5),
173                (3, 4),
174                (3, 5),
175                (4, 5),
176            ],
177        ))),
178        optimal_config: vec![0, 1, 2, 0, 2, 1],
179        optimal_value: serde_json::json!(3),
180    }]
181}
182
183#[cfg(test)]
184#[path = "../../unit_tests/models/graph/maximum_domatic_number.rs"]
185mod tests;